Will Tseng, Author at Perficient Blogs https://blogs.perficient.com/author/wtseng/ Expert Digital Insights Tue, 24 Feb 2015 12:00:21 +0000 en-US hourly 1 https://blogs.perficient.com/files/favicon-194x194-1-150x150.png Will Tseng, Author at Perficient Blogs https://blogs.perficient.com/author/wtseng/ 32 32 30508587 Azure Search: Scoring Profiles https://blogs.perficient.com/2015/02/24/azure-search-scoring-profiles/ https://blogs.perficient.com/2015/02/24/azure-search-scoring-profiles/#respond Tue, 24 Feb 2015 12:00:21 +0000 http://blogs.perficient.com/microsoft/?p=25675

Introduction

When a search query is submitted to the index, each document that is returned has a search scoreazuresearch_configure1_5_searchtile which is an indicator of its relevance in the current search query and context. The higher the score, the more relevant the item and therefore, the higher it is ranked on a scale of high to low.
In Azure Search, you can tweak the calculation of a search score through an index modification called a scoring profile. A common usage of scoring profiles is Geo-search, which allows you to automatically boost items which are closer to the location of the user. You can also simply boost by pushing newer documents to the top of your search results, or in some cases boost some older documents. It all depends on what your business needs are.
You can configure as many scoring profiles as you would like in your search index, but you can only specify one profile at a time when running a query.

Scoring Profiles vs. Managed Property Weighting and XRANK

For the SharePoint Devs out there getting into Azure Search, Scoring Profiles is a lot like Managed Property Weighting combined with XRANK in SharePoint. However, I find that Azure Search gives you control that allows you too really customize your boosting in ways that SharePoint cannot. Most of your boosting control comes in how you define your scoring profiles in your index, which allows you to really clean up your query on the front end without having to use XRANK. For example, to achieve a simple Geo-search, you would only need to provide the scoring profile and the current location as parameters in your search query.
As a further bonus, you can configure as many different scoring profiles as you would like, giving you full control of how your query gets processed. While in SharePoint, you can only configure a single set of relevancy rules without using XRANK, in Azure Search you can configure as many as you would like and specify which one you would like to use at the time. This way you can specify different weights for different fields (managed properties) when your business needs change without having to completely clobber the back-end index. 

In Conclusion…

Having the ability to change scoring profiles as your user navigates through different portions of your website gives you a great flexibility. You can control a lot of what occurs in your search queries by modifying the scoring profiles in the index, rather than piece together complex queries. It is simply a cleaner way of making things happen.
Stay tuned for some physical examples! For a detailed msdn article, please go here.

]]>
https://blogs.perficient.com/2015/02/24/azure-search-scoring-profiles/feed/ 0 224886
Microsoft Azure Search Preview https://blogs.perficient.com/2015/02/12/microsoft-azure-search-preview/ https://blogs.perficient.com/2015/02/12/microsoft-azure-search-preview/#respond Thu, 12 Feb 2015 12:00:45 +0000 https://blogs.perficient.com/microsoft/?p=25525

Search as a Serviceazuresearch_configure1_5_searchtile

From FAST ESP to SharePoint Search and Bing/Google, search has become an integral point for users to reference their data. Microsoft has developed Azure Search to provide an integration point for a complete search experience. Developers can use the Azure portal and front-end APIs to tune their search index as well as increase and decrease their index capacity, query count, and number of documents. These features allow for individualized, cost effective solutions for all of your search scenarios.
There are already some established solutions such as Apache Solr and Elasticsearch (which is the platform that Azure Search is based on), however ensuring cost-effective scalability and steady hosting can be a whole other task completely. Azure Search’s pricing can be scaled by your query traffic as well as you document count, which allows you to pay for what you use. In the end, all are viable search solutions for your business’ needs.

Azure Search Under the Hood

Document – A document is simply a single entity in the search index. Documents can be a webpage, a physical document like a PDF or Word document, or any custom fed content that the user can format.
Search Index – The purpose of the index is to store a collection of documents, and optimize speed and performance for retrieving documents (documents = an item in the database). The index schema is defined by the user. You can have multiple indexes in your Azure Search environment.
Query Processing – The Query processor for each search engine is tuned uniquely. It is responsible for translating the query syntax before sending it to the index for document retrieval.
Indexing Component – The indexing component is responsible for processing the data before sending the data to the search index. This is commonly known as a pipeline or enrichment step which is responsible for massaging or normalizing data.

Azure Search (vs. SharePoint Search)

The core of Azure Search is built off of Elasticsearch, but don’t be fooled, this is not just Elasticsearch hosted on Azure. Microsoft has provided their own API on top of it, which makes interfacing with the engine more familiar to Microsoft developers and front-end developers.
If you were a PowerShell heavy SharePoint developer who used PowerShell scripting for configuration, then Azure Search’s APIs should feel familiar. However, Azure Search does not include a crawler, which SharePoint users have become accustomed to. Developers are responsible for formatting and feeding the data to the document processor/indexer. While this might seem like a huge oversight, content feeders or crawlers are generally not included in stand-alone search engines like Apache Solr.

Why Azure Search?

Search infrastructure has always been both taxing to maintain and often not cost efficient to scale. Azure search infrastructure is fully managed in the cloud by Microsoft, leaving you bandwidth to build you application. As your search application grows and requires more bandwidth, you can comfortably move up the pricing tier, or move down tiers during the off-season.
The management of the data has all been moved to the front end, exposed through a JSON schema API. There are no predefined index schemas or a crawler to fetch data. This is a “push-based” indexing system, which is common for engines built off Lucene. This allows you to easily separate your data pulling and pushing onto other servers and place less stress on your actual search servers. This also gives you full control on when content is pushed and what content is pushed.

So What’s Next?

Go try it out! http://azure.microsoft.com/en-us/services/search/
If you have feedback for Microsoft or would like to see what others are saying, please visit: http://feedback.azure.com/forums/263029-azure-search
 

]]>
https://blogs.perficient.com/2015/02/12/microsoft-azure-search-preview/feed/ 0 224878
SharePoint 2013 Search: External Content Relevancy Boosting https://blogs.perficient.com/2015/02/02/sharepoint-2013-search-external-content-relevancy-boosting/ https://blogs.perficient.com/2015/02/02/sharepoint-2013-search-external-content-relevancy-boosting/#comments Tue, 03 Feb 2015 03:39:12 +0000 https://blogs.perficient.com/microsoft/?p=25277

Where’s my External Content?

The first time you bring in external content into your search index, whether that be from BCS or Custom Connector, your search ranking and relevance for that content will most likely be buried under all of your SharePoint content. This is quite common. Don’t worry however, we will go over some of your options to bring that content to the top, also known as relevancy boosting.

Query Rules for the Admins

It has been said and it has been done a lot. Query Rules have become somewhat of a staple in SharePoint 2013. I won’t cover it in detail because there’s a huge amount of resources out there (check out Chris Hines’ post here), however I will leave a small tidbit below.
Often when you are pulling multiple entities from BCS, you end up with a single content source with quite a few different entities that are of different types of content. Good news is that you can split them out.

Accessing Individual BCS Entities

Look for a crawled property called EntityName (might be two of them), and map them to a managed property. For our sake, we will call it MPEntityName. You will want this managed property to be queryable.
Then you can query your BCS entity by running a simple MPEntityName:NameOfEntity.

Managed Property Advanced Searchable Settings

By default a managed property that you create is set to “Context 0” which means that it is only used for recalling search results and does not influence ranking. If your title or description of your External Content is being indexed but not influenced by ranking, then there is a high chance that the ranking will get buried by other managed properties like SharePoint’s OOTB Title field.
To change the weight group of the managed property, simply edit the managed property and find the “Advanced Searchable Settings”.
2015-02-02_21h38_05
 
2015-02-02_21h37_57
 
From there modify the weight group to suit your needs. Below is a chart provided by Microsoft to determine relative weight.
 
2015-02-02_21h36_38

XRANK (When the above just isn’t enough)

The story of XRANK needs a whole blog to itself. However, let’s take a look at how to essentially force items up to the top of the search stack.
Query:
({searchBoxQuery} XRANK(cb=5000) MPEntityName:NameOfEntity)
This query will simply boost all of the entity matched results on all search queries. Keep in mind a boost 5000 (cb=5000) will generally shove everything up to the top of the search results stack.

Conclusion

Between these three options, you should be able to interlace your External Content with your standard SharePoint content, and improve your overall search relevancy on External Content.

]]>
https://blogs.perficient.com/2015/02/02/sharepoint-2013-search-external-content-relevancy-boosting/feed/ 2 224868
Nintex 2013: Sequential Approval Workflow https://blogs.perficient.com/2014/02/06/nintex-2013-sequential-approval-workflow/ https://blogs.perficient.com/2014/02/06/nintex-2013-sequential-approval-workflow/#comments Thu, 06 Feb 2014 22:02:08 +0000 https://blogs.perficient.com/microsoft/?p=20982

I had a recent request from a client to create a workflow that would only create approval task in the order that the approvers were listed. This allows the approval tasks to be created one at a time rather than all at once, and if a single approver breaks the chain, we can either break the workflow or escalate a change.
While this can be done in custom code or closely in SharePoint Designer, Nintex 2013 allows power users and power admins to create powerful workflows through the browser. This allows for changes and deployment of workflows to occur without the need for solution deployments.

Workflow

2014-02-06_12h24_44
 

Workflow Variables

Create the following workflow variables.
2014-02-06_14h13_24
 

Create a Regular Expression

In order to create the Foreach loop, we need to populate a variable of type Collection with the users that will be approving the workflow. For users populated from the SharePoint People Picker field, use a semi-colon “;” as your split pattern. For the input text, use the “Insert Reference” to apply the column containing the Approvers to the Input Text.
2014-02-06_14h06_31
2014-02-06_14h03_13
 

For each Loop

Now that we have the Regular Expression which creates the collection which can be parsed by the loop. We use the userCollection as the target collection which stores the individual user that is the current approve of the workflow process.
2014-02-06_14h21_34
2014-02-06_14h24_26

]]>
https://blogs.perficient.com/2014/02/06/nintex-2013-sequential-approval-workflow/feed/ 1 224565
SharePoint 2013 Search: JavaScript CSOM Primer https://blogs.perficient.com/2014/01/14/sharepoint-2013-search-javascript-csom-primer/ https://blogs.perficient.com/2014/01/14/sharepoint-2013-search-javascript-csom-primer/#comments Tue, 14 Jan 2014 21:07:39 +0000 https://blogs.perficient.com/microsoft/?p=20718

A lot of JavaScript incoming!
With the rise of SharePoint Online, custom search solutions through JavaScript have become a staple for the development work for clients. While a lot of your search basics can be handled through simple modification of Display Templates (see some of my previous blogs for some basics there), the opportunity still arises when you need to do something truly custom with your search code.
A lot of the basics are already out there for querying the Client Side Object Model through JavaScript (see this MSDN post by Cory Roth). We will review those, and take it a little deeper in order to give you a little more control over your queries and results returned.
We will cover:

  • The essentials and basics
  • Retrieving custom retrievable Managed Properties
  • Sorting by Managed Properties
  • Increase the Row Limit (results returned)
  • Setting a specific Result Source
  • Set Trim Duplicates to False

The Basics

The great part about all of the new 2013 Client Side tools is that you can just open up a Script Editor or a Content Editor Web Part and get started instantly. No need to deploy any managed code. If there’s items in your SharePoint search index and you have permission to access them, you can jump right in and get started.
Throw a div on the page with the ID “resultsDiv”, and see your table appear when you execute the JavaScript code. If you want to go deeper, simply add a button and a textbox to your page as well and replace the appropriate portions of the code.
To change the search query, just change the following line:

keywordQuery.set_queryText("New Query Text Here");
   1:  var context = SP.ClientContext.get_current();
   2:  var keywordQuery = new Microsoft.SharePoint.Client.Search.Query.KeywordQuery(context);
   3:  keywordQuery.set_queryText("Search Query");
   4:  var searchExecutor = new Microsoft.SharePoint.Client.Search.Query.SearchExecutor(context);
   5:  var results = searchExecutor.executeQuery(keywordQuery);
   6:  context.executeQueryAsync(onQuerySuccess, onQueryFail);
   7:   
   8:  function onQuerySuccess() {
   9:       $("#resultsDiv").append('<table>');
  10:       if (results.m_value.ResultTables) {
  11:          $.each(results.m_value.ResultTables, function(index, table) {
  12:              if(table.TableType == "RelevantResults") {
  13:                  $.each(results.m_value.ResultTables[index].ResultRows, function () {
  14:                        $("#resultsDiv").append('<tr>');
  15:                        $("#resultsDiv").append('<td>' + this.Title + '</td>');
  16:                        $("#resultsDiv").append('<td>' + this.Author + '</td>');
  17:                        $("#resultsDiv").append('<td>' + this.Write + '</td>');
  18:                        $("#resultsDiv").append('<td>' + this.Path + '</td>');
  19:                        $("#resultsDiv").append('</tr>');
  20:                     })
  21:                  }
  22:              });
  23:          }
  24:          $("#resultsDiv").append('</table>');
  25:  }
  26:  
  27:  function onQueryFail(){
  28:      alert('Query failed. Error:' + args.get_message());
  29:  }

Retrieving Custom Managed Properties

In the request to the search index, there is a default set of managed properties that are returned by the index. In order to return other managed properties, we must request them.

   1:  var context = SP.ClientContext.get_current();
   2:  var keywordQuery = new Microsoft.SharePoint.Client.Search.Query.KeywordQuery(context);
   3:  keywordQuery.set_queryText("Search Query");
   4:  
   5:  // Set Properties
   6:  var properties = keywordQuery.get_selectProperties();
   7:  properties.add('Size');
   8:  properties.add('CustomManagedProperty');
   9:   
  10:  var searchExecutor = new Microsoft.SharePoint.Client.Search.Query.SearchExecutor(context);
  11:  var results = searchExecutor.executeQuery(keywordQuery);
  12:  context.executeQueryAsync(onQuerySuccess, onQueryFail);
  13:   
  14:  function onQuerySuccess() {
  15:       $("#resultsDiv").append('<table>');
  16:       if (results.m_value.ResultTables) {
  17:          $.each(results.m_value.ResultTables, function(index, table) {
  18:              if(table.TableType == "RelevantResults") {
  19:                  $.each(results.m_value.ResultTables[index].ResultRows, function () {
  20:                        $("#resultsDiv").append('<tr>');
  21:                        $("#resultsDiv").append('<td>' + this.Title + '</td>');
  22:                        $("#resultsDiv").append('<td>' + this.Author + '</td>');
  23:                        $("#resultsDiv").append('<td>' + this.Write + '</td>');
  24:                        $("#resultsDiv").append('<td>' + this.Path + '</td>');
  25:   
  26:                        // Get Data from custom returned properties
  27:                        $("#resultsDiv").append('<td>' + this.Size + '</td>');
  28:                        $("#resultsDiv").append('<td>' + this.CustomManagedProperty + '</td>');
  29:   
  30:                        $("#resultsDiv").append('</tr>');
  31:                     })
  32:                  }
  33:              });
  34:          }
  35:          $("#resultsDiv").append('</table>');
  36:  }

Sorting by Managed Property

Sorting is fairly straightforward. Choose the Managed Property that you would like to sort by (make sure it’s set as Sortable!). Choose whether it needs to be Ascending or Descending. Ascending is 0, Descending is 1.

   1:  var context = SP.ClientContext.get_current();
   2:  var keywordQuery = new Microsoft.SharePoint.Client.Search.Query.KeywordQuery(context);
   3:  keywordQuery.set_queryText("Search Query");
   4:  
   5:  // Sorting (Ascending = 0, Descending = 1)
   6:  keywordQuery.set_enableSorting(true);
   7:  var sortproperties = keywordQuery.get_sortList();
   8:  sortproperties.add("Write", 0);
   9:   
  10:  var searchExecutor = new Microsoft.SharePoint.Client.Search.Query.SearchExecutor(context);
  11:  var results = searchExecutor.executeQuery(keywordQuery);
  12:  context.executeQueryAsync(onQuerySuccess, onQueryFail);

Increase the Number of Results Returned

The default number of results returned is 50 and is capped at 500. You can increase the cap through PowerShell.

   1:  var context = SP.ClientContext.get_current();
   2:  var keywordQuery = new Microsoft.SharePoint.Client.Search.Query.KeywordQuery(context);
   3:  keywordQuery.set_queryText("Search Query");
   4:  
   5:  // Set Row Limit
   6:  keywordQuery.set_rowLimit(500);
   7:   
   8:  var searchExecutor = new Microsoft.SharePoint.Client.Search.Query.SearchExecutor(context);
   9:  var results = searchExecutor.executeQuery(keywordQuery);
  10:  context.executeQueryAsync(onQuerySuccess, onQueryFail);

Setting A Specific Result Source

You will need the GUID of the Result Source.

   1:  var context = SP.ClientContext.get_current();
   2:  var keywordQuery = new Microsoft.SharePoint.Client.Search.Query.KeywordQuery(context);
   3:  keywordQuery.set_queryText("Search Query");
   4:  
   5:  // Set Source ID (GUID)
   6:  keywordQuery.set_sourceId("XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX");
   7:   
   8:  
   9:  var searchExecutor = new Microsoft.SharePoint.Client.Search.Query.SearchExecutor(context);
  10:  var results = searchExecutor.executeQuery(keywordQuery);
  11:  context.executeQueryAsync(onQuerySuccess, onQueryFail);

Trim Duplicates

In the potential rare instance that you will need to see duplicates, you can set Trim Duplicates to false.

   1:  var context = SP.ClientContext.get_current();
   2:  var keywordQuery = new Microsoft.SharePoint.Client.Search.Query.KeywordQuery(context);
   3:  keywordQuery.set_queryText("Search Query");
   4:  
   5:  // Set Trim Duplicates to False
   6:  keywordQuery.set_trimDuplicates(false);
   7:   
   8:  var searchExecutor = new Microsoft.SharePoint.Client.Search.Query.SearchExecutor(context);
   9:  var results = searchExecutor.executeQuery(keywordQuery);
  10:  context.executeQueryAsync(onQuerySuccess, onQueryFail);
]]>
https://blogs.perficient.com/2014/01/14/sharepoint-2013-search-javascript-csom-primer/feed/ 1 224546
The Search Engine for Your Business: Part 1 https://blogs.perficient.com/2013/07/15/the-search-engine-for-your-business-part-1/ https://blogs.perficient.com/2013/07/15/the-search-engine-for-your-business-part-1/#respond Mon, 15 Jul 2013 18:59:46 +0000 https://blogs.perficient.com/microsoft/?p=18722

Introduction

It has been a while since I’ve hit the blogosphere. During that time I had the pleasure of presenting a search webinar speaking about SharePoint 2013 search. Not only did I learn that presenting for a webinar was much different than presenting in front of a room of people, I also realized that the divide between business and technical search during the webinar was greater than I believed.
Let me elaborate. I had badly assumed that many people who had attended were both business and technical, but who had decided and knew exactly what they wanted to do with search, and that all they wanted to see was if SharePoint 2013 was the answer to their business. So my presentation tailored heavily to the feature-friendly side of SharePoint 2013 search and spoke of a lot of technical, administrative benefits of the platform which perhaps isolated business users who were there to just learn about search in general.
Here is my response to your questions and interests! This will be the beginning of a multi-part blog which will dive into what you and your business should know when selecting your search engine. Starting from the core, we will look at what features have been crucial to clients as well as what you should look for in an indexer. SharePoint 2013 will be used heavily as an example here, but it’s up to you to do due diligence on your research!

upward-graphWhat is Your Business Goal?

So what are you doing, or want to do with search?
Part of what makes search projects so interesting is that every client envisions and comes up with their idea of what their ideal search platform looks like. While we at Perficient can help guide your solution to completion, the idea is still yours. Start with something that you would like to find easier, and let that seed grow into your potential business platform.

  • Intranet Search
  • Product Catalog
  • Legal Documents
  • Medical Journals
  • Research Data
  • Business Data
  • People Directory
  • News and News Archive
  • Publishing
  • and so… so much more!

What does my Business Gain?

Generally this question is asked with dollar signs attached to it. It is an important question no doubt, and if I could answer it with graphs and charts, then I wouldn’t be writing this blog.
What your business gains can be answered with a balance of two points. How efficient your search solution is in delivering correct and relevant results and how many business users are using it every day. If you can nail both points with your solution then you have a solution which has potentially infinitesimal returns. Although if you truly want to measure your returns, a calculation of the time spent looking for something the “old” way versus the “new” search solution will give you an semi-accurate count.
Of course there is the risk that you will deploy your solution and either nobody will use it, or your search results won’t be accurate (and in return nobody will used it). If your solution does return accurate results and is faster than your old methods, but users aren’t using it, then normally a good nudge as well as a quick 15 minute training will hopefully convert some non-believers.

Where to Start?3-questionsOF

If you don’t have any ideas, then look for areas in your business where people struggle to find data. Look for places where a simple free-text search with refinement could be faster than what your current solution does now. Look especially at areas that your business could truly gain value from having something done faster.
If you’ve already got a concept, then great! Now you have to refine your concept. I would recommend looking at search engines that other companies have implemented. Nearly every major retailer has some sort of search engine that you can view to get ideas. Look for things that are done well, and things that you don’t like. Pay close attention to the behavior of the search experience, from how results load on the page to how refiners behave (multi-select, narrowing, result counts, etc).
Finally, if you know what features you want and have a design on how you want your search experience to behave, then it’s time to select your search engine to build your platform upon. Hopefully the following blogs will point you towards the features that you want, and help you understand how important the behavior of each feature can make or break your budget.

]]>
https://blogs.perficient.com/2013/07/15/the-search-engine-for-your-business-part-1/feed/ 0 224381
SP2013 Search: Workaround for People Result Sorting https://blogs.perficient.com/2013/03/19/sp2013-search-workaround-for-people-result-sorting/ https://blogs.perficient.com/2013/03/19/sp2013-search-workaround-for-people-result-sorting/#respond Tue, 19 Mar 2013 15:37:44 +0000 https://blogs.perficient.com/microsoft/?p=17623

So I was getting some unexpected behavior (as in just not working!) when I tried to apply sorting to people search from the peopleresults.aspx page through the query builder. I hadn’t had any issues sorting on my other result sources but people results were proving to be stubborn.
To reproduce, try going to your People search vertical which is generally peopleresults.aspx page, edit the people search results web part, and open up the sort section of the query builder. Try sorting on a field (FirstName or LastName would be a good one).

Workaround

Create a new People Result Source

The solution is simple enough, instead of modifying the query on the page, create a new people result source.
1. Make a Copy of the existing result source

  • Go to Site Settings –> Result Sources to access your site collection level result sources
  • Give it a name, such as Sorted People Results
  • Find the Query Transform and Launch the Query Builder
    • Open the Sorting Tab and sort by whatever field you choose

     

  • Leave everything else as is and save your new Result Source

2. Go back to your people results page, open the query builder, and change your result source to the one you just created. Your results should now be sorted!
2013-03-19_1031
 
2013-03-19_1034
 
Of course after I figured it out, I went and Binged/Googled the issue and found that someone had already asked. Guess I should’ve used search to solve the problem! TechThread
 

]]>
https://blogs.perficient.com/2013/03/19/sp2013-search-workaround-for-people-result-sorting/feed/ 0 224295
SP2013 Search–Content Type Managed Property in Queries https://blogs.perficient.com/2013/03/06/sp2013-search-content-type-managed-property-in-queries/ https://blogs.perficient.com/2013/03/06/sp2013-search-content-type-managed-property-in-queries/#respond Wed, 06 Mar 2013 17:43:34 +0000 http://blogs.perficient.com/microsoft/?p=17400

Not the right effect?

Very short and sweet this time, hopefully will help those who happen on this page for this common issue.
In the Query Builder, you can create a query using using fields or managed properties. A common field that users will use for creating a fielded search is Content Type. However, as you can see below, the out of the box effect for the content type property filter doesn’t quite have the proper effect.
 
2013-03-05_0029

The right stuff:

The change here is quite simple. Change ContentType to ContentTypeId in your query. Since the referenced value is the content type id, changing the managed property to the correct one makes sense. However, you cannot get the content type id value if you try to add ContentTypeId from the property filter menu. You must first add the ContentType and value then change ContentType to ContentTypeId.
SharePoint creates the Content Type hierarchy by concatenating numerical values together to create the content type id. The deeper the child from the parent, generally the longer its content type id is.
2013-03-05_0035

Going the Other Direction

In the example above, using the content type id also brings in child items. If you had a parent content type “Article Page”, and a child content type underneath it, using the above query would return the parent and its children.
To return only the parent, you can use the query ContentType:”Article Page”, which will return only the content type of Article Page. I’m still trying to figure out why someone couldn’t just get rid of the wildcard at the end of the content type id… but perhaps that for another blog.

]]>
https://blogs.perficient.com/2013/03/06/sp2013-search-content-type-managed-property-in-queries/feed/ 0 224281
Retrieving SharePoint List Column Managed Metadata (JavaScript) https://blogs.perficient.com/2013/02/28/retrieving-sharepoint-list-column-managed-metadata-javascript/ https://blogs.perficient.com/2013/02/28/retrieving-sharepoint-list-column-managed-metadata-javascript/#respond Thu, 28 Feb 2013 22:25:44 +0000 http://blogs.perficient.com/microsoft/?p=17356

This is a quickie, but can be useful for those accessing SharePoint Managed Metadata from the Client Side.
Often when you’re pulling back data from SharePoint lists, you’ll get a full-fledged object back rather than a single field value (and yes I ran into this while working on some fancy calls in a Display Template).
2013-02-28_1544
The code itself is fairly simple, but here’s how you can go digging for an answer yourself. Go to your browser’s debugging console (F12) and inspect the JSON response from the Console for your function. Here, find the correct response for your call that contains the items from the list that you queried. From there, you can see how the objects are structured and how to access the managed metadata field properly. In the picture below, you can see where I have highlighted the metadata field that I am looking for which has a type of SP.Taxonomy.TaxonomyFieldValue.
Hope this helps you guys dig through the response from SharePoint and get you the right field values! Helpful MSDN blog to get you started with client side calls.
2013-02-28_1553
 
Now that you see the structure, the import part of your code is this (after you instantiate the enumerator to parse the items returned from the list):

   1: while (listEnumerator.moveNext()) {

   2: var keyword = listEnumerator.get_current().get_item("TileID")["Label"];

   3: }

And the complete code here:

   1: var ctx = new SP.ClientContext();

   2: var targetList = ctx.get_web().get_lists().getByTitle('ListName');

   3: var queryItems;

   4:

   5: var query = new SP.CamlQuery();

   6: query.set_viewXml("<View><Query><Where><Contains><FieldRef Name='Id'/><Value Type='Text'>FilterValue</Value></Contains></Where></Query></View>");

   7:

   8: queryItems = targetList.getItems(query);

   9: ctx.load(mandatoryItems);

  10: ctx.executeQueryAsync(onGetSucceeded, onGetFailed);

  11:

  12: function onGetSucceeded()

  13: {

  14:     var listEnumerator = queryItems.getEnumerator();

  15:

  16:     while (listEnumerator.moveNext()) {

  17:         var keywords = listEnumerator.get_current().get_item("TileID")["Label"];

  18:         alert(childItem);

  19:     }

  20: }

  21: function onGetFailed(sender, args) {

  22:   alert('Request failed. \nError: ' + args.get_message() + '\nStackTrace: ' + args.get_stackTrace());

  23: }

]]>
https://blogs.perficient.com/2013/02/28/retrieving-sharepoint-list-column-managed-metadata-javascript/feed/ 0 224274
SP2013 Search DYI Display Template https://blogs.perficient.com/2013/02/21/sp2013-search-dyi-display-template/ https://blogs.perficient.com/2013/02/21/sp2013-search-dyi-display-template/#respond Fri, 22 Feb 2013 05:30:48 +0000 https://blogs.perficient.com/microsoft/?p=17255

For this project, I highly recommend these tools (in a slightly sarcastic manner!):

  • A Highlighter (your mouse)
  • A pair of scissors or a copy machine (cut/copy)
  • Some glue (paste)
  • Some pieces of pre-constructed SharePoint  (good ol’ SharePoint 2013)
  • Your own paper (favorite text editor)

Today, we are going to create our own custom Hover Panel Display Template. I chose the Hover Panel for this demonstration simply because you have to do a good copy and paste job three times… and once you get the Hover Panel Display Template, then the Results Display Template follows the same model.

Let’s Craft (or something of the sort)!

Before we create our Display Template, we need to obtain some pieces from SharePoint. Go to the site settings where you are hosting your search center here: Site Settings –> Master Pages and Page Layouts –> Display Templates –> Search. You will need to download a local copy of the following files:

defaulthoverpanel

and

commonhoverpanel

After you have all of that, let’s open up Item_Default_HoverPanel.html. Here you will find the base Hover Panel markup that we will be using. In your text editor, click SAVE AS to save the file under a new filename. This way we won’t overwrite the old Item_Default_HoverPanel.html in the display template library.

2013-02-21_1330

1. Change the title here. This will be how your item is displayed when selecting a specific display template.

2. Give it a short description

3. ManagedPropertyMapping (optional): We will go over in more detail in another blog how to properly surface managed properties. In short however, for example, add this ‘ManagedProperty1’:’ManagedProperty1’ inside the tags.

4. You should change the div id to something related to your item type.

5. ctx.RenderHeader(ctx) –> For this, you’ll need parts of Item_CommonHoverPanel_Header.html.

  • In your custom Hover Panel, delete _#= ctx.RenderHeader(ctx) =#_
  • Get those scissors and glue and cut/copy the following section from Item_CommonHoverPanel_Header.html.
    • The section in grey highlighted below is what you need to copy. Between the div Item_CommonHovePanel_Header.

2013-02-21_2259

  • Paste the code you copied into the div where you removed _#= ctx.RenderHeader(ctx) =#_

2013-02-21_2319

 

6 and 7

  • Rinse and repeat for _#= ctx.RenderBody(ctx) =#_ and _#= ctx.RenderFooter(ctx) =#_
    • _#= ctx.RenderBody(ctx) =#_ goes with Item_CommonHoverPanel_Body.html
    • _#= ctx.RenderFooter(ctx) =#_ goes with Item_CommonHoverPanel_Actions.html

In the end, you should end up with a file that looks something similar to this:

Full Item Hover Panel Code:

   1: <html xmlns:mso="urn:schemas-microsoft-com:office:office" xmlns:msdt="uuid:C2F41010-65B3-11d1-A29F-00AA00C14882"> 

   2: <head>

   3: <title>Modified Hover Panel</title>

   4:  

   5: <!--[if gte mso 9]><xml>

   6: <mso:CustomDocumentProperties>

   7: <mso:TemplateHidden msdt:dt="string">0</mso:TemplateHidden>

   8: <mso:MasterPageDescription msdt:dt="string">Displays the modified hover panel template.</mso:MasterPageDescription>

   9: <mso:ContentTypeId msdt:dt="string">0x0101002039C03B61C64EC4A04F5361F385106603</mso:ContentTypeId>

  10: <mso:TargetControlType msdt:dt="string">;#SearchHoverPanel;#</mso:TargetControlType>

  11: <mso:HtmlDesignAssociated msdt:dt="string">1</mso:HtmlDesignAssociated>

  12: <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'</mso:ManagedPropertyMapping>

  13: </mso:CustomDocumentProperties>

  14: </xml><![endif]-->

  15: </head>

  16: <body>

  17:     <div id="Item_Modified_HoverPanel">

  18: <!--#_

  19:         var i = 0;

  20:         var id = ctx.CurrentItem.csr_id;

  21:         ctx.CurrentItem.csr_ShowViewLibrary = !Srch.U.isWebPage(ctx.CurrentItem.FileExtension);

  22:         if(ctx.CurrentItem.IsContainer)

  23:         {

  24:             ctx.CurrentItem.csr_FileType = Srch.Res.ct_Folder

  25:         }

  26: 

  27:         ctx.currentItem_ShowChangedBySnippet = true;

  28: 

  29: _#-->

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

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

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

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

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

  35:                     <!-- _#= ctx.RenderHeader(ctx) =#_ -->

  36:                     <!--#_

  37:         var id = ctx.CurrentItem.csr_id;

  38:         var fileType = ctx.CurrentItem.csr_FileType;

  39:         if($isEmptyString(fileType)){

  40:             fileType = HP.GetFriendlyNameForFileType(ctx.CurrentItem);

  41:         }

  42:         var showFileIcon = Boolean(ctx.CurrentItem.csr_ShowFileIcon);

  43:         var titleCSSClass = "ms-srch-hover-title ms-dlg-heading ms-srch-ellipsis";

  44:         var maxViews = 10000;

  45: _#-->

  46:         <div>

  47:             <div class="ms-srch-hover-close">

  48:                 <a href="javascript:HP.Close()" id="_#= $htmlEncode(id + HP.ids.close) =#_" title="_#= $htmlEncode(Srch.Res.hp_Tooltip_Close) =#_">

  49:                     <img class="js-callout-closeButtonImage" src="_#= $urlHtmlEncode(GetThemedImageUrl('spcommon.png')) =#_" alt="_#= $htmlEncode(Srch.Res.hp_Tooltip_Close) =#_" />

  50:                 </a>

  51:             </div>

  52: <!--#_

  53:             if (showFileIcon) {

  54: _#-->

  55:                 <img class="ms-srch-item-icon ms-positionRelative" id="_#= $htmlEncode(id + Srch.U.Ids.icon) =#_" onload="this.style.display='inline';this.style.bottom='-5px'" src="_#= $urlHtmlEncode(Srch.U.getIconUrlByFileExtension(ctx.CurrentItem)) =#_" />

  56: <!--#_

  57:             }    

  58:             if(!$isNull(ctx.CurrentItem.ViewsLifeTime)) {

  59:                 var formattedViewsLifeTime;

  60: 

  61:                 if (ctx.CurrentItem.ViewsLifeTime > maxViews)    {

  62:                     formattedViewsLifeTime = maxViews.localeFormat("N0") + "+";  

  63:                 } 

  64:                 else {

  65:                     formattedViewsLifeTime = ctx.CurrentItem.ViewsLifeTime.localeFormat("N0"); 

  66:                 }

  67:                 var views = "";

  68:                 titleCSSClass = titleCSSClass + " ms-srch-hover-title-extraMarginForViews";

  69:                 if (ctx.CurrentItem.ViewsLifeTime === 1) {

  70:                     views = Srch.Res.hp_Views_Singular;

  71:                 } 

  72:                 else {

  73:                     views = Srch.Res.hp_Views_Plural;

  74:                 }                   

  75:                 var tooltipViewsEncoded = $htmlEncode(String.format(Srch.Res.hp_Tooltip_Views, formattedViewsLifeTime));

  76: _#-->            

  77:                 <div class="ms-srch-hover-popularity ms-metadata" title="_#= tooltipViewsEncoded =#_ ">        

  78:                     <div id="_#= $htmlEncode(id + HP.ids.viewsLifeTime) =#_"> _#= $htmlEncode(formattedViewsLifeTime) =#_ </div>

  79:                     <div> _#= $htmlEncode(views) =#_ </div>

  80:                 </div>

  81: <!--#_

  82:             }

  83:             if (!Srch.U.e(ctx.CurrentItem.Title)) {

  84: _#-->        

  85:                 <div class="_#= titleCSSClass =#_" id="_#= $htmlEncode(id + HP.ids.title) =#_"  title="_#= $htmlEncode(ctx.CurrentItem.Title) =#_">

  86:                     _#= $htmlEncode(ctx.CurrentItem.Title) =#_

  87:                 </div>

  88: <!--#_                        

  89:             }

  90: _#-->    

  91:         </div>

  92:         <div class="ms-metadata">

  93:             <div class="ms-srch-hover-filetype">

  94: <!--#_

  95:                 if (!Srch.U.e(fileType) && $isNull(ctx.CurrentItem.ServerRedirectedEmbedURL)) {

  96: _#-->

  97:                     <span id="_#= $htmlEncode(id + HP.ids.fileType) =#_">

  98:                         _#= $htmlEncode(fileType) + " " =#_

  99:                     </span>

 100: <!--#_

 101:                 }                

 102: _#-->

 103:             </div>

 104:         </div>

 105: <!--#_

 106: 

 107:     AddPostRenderCallback(ctx, function(){

 108:         if (Srch.U.e(ctx.CurrentItem.ServerRedirectedEmbedURL)){

 109:             Srch.U.logClick($get(id), "Hover");

 110:         } else {

 111:             Srch.U.logClick($get(id), "HoverWithWAC");

 112:         }

 113:     });

 114: 

 115: _#-->

 116:                 </div>

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

 118:                     <!-- _#= ctx.RenderBody(ctx) =#_ -->

 119:                     <!--#_

 120:         var id = ctx.CurrentItem.csr_id;

 121:         var dataShown = false;

 122:         var changedBySnippetShown = false;

 123: 

 124:         if (ctx.currentItem_ShowChangedBySnippet &&

 125:             !Srch.U.n(ctx.CurrentItem.LastModifiedTime) &&

 126:             !$isEmptyString(ctx.CurrentItem.EditorOWSUSER))

 127:         {

 128:             var changedById = id + "_ChangedBy";

 129:             var editorIdentifiers = ctx.CurrentItem.EditorOWSUSER.split(" | ");

 130:             var editorEmail = "";

 131:             if(!$isNull(editorIdentifiers[0]))

 132:             {

 133:                 editorEmail = editorIdentifiers[0];

 134:             }

 135: 

 136:             var editorDisplayName = "";

 137:             if(!$isNull(editorIdentifiers[1]))

 138:             {

 139:                 editorDisplayName = editorIdentifiers[1];

 140:             }

 141: 

 142:             AddPostRenderCallback(ctx, function()

 143:             {

 144:                 EnsureScriptFunc("clienttemplates.js", "RenderUserFieldWorker", function()

 145:                 {

 146:                     EnsureScriptFunc("sp.datetimeutil.js", "SP.DateTimeUtil.SPRelativeDateTime", function()

 147:                     {

 148:                         var renderCtx = new ContextInfo();

 149:                         renderCtx.Templates = {};

 150:                         renderCtx.Templates["Fields"] = {};

 151: 

 152:                         var field =

 153:                         {

 154:                             "FieldType" : "User",

 155:                             "DefaultRender" : "1",

 156:                             "HasUserLink" : "1",

 157:                             "Type" : "User"

 158:                         };

 159: 

 160:                         var listItem =

 161:                         {

 162:                             "id" : "",

 163:                             "title" : editorDisplayName,

 164:                             "email" : editorEmail

 165:                         };

 166: 

 167:                         var listSchema =

 168:                         {

 169:                             "EffectivePresenceEnabled" : "1",

 170:                             "PresenceAlt" : "No presence information",

 171:                             "UserDispUrl" : "/_layouts/15/userdisp.aspx"

 172:                         };

 173: 

 174:                         var htmlUserPresence = RenderUserFieldWorker(renderCtx, field, listItem, listSchema);

 175:                         var convertedDate = SP.DateTimeUtil.IntlDate.createFromJsLocalDate(ctx.CurrentItem.LastModifiedTime, Srch.U.getCalendarType());

 176:                         var changedDate = convertedDate.format("d", Sys.CultureInfo.CurrentCulture.name);

 177:                         var changedTime = convertedDate.format("t", Sys.CultureInfo.CurrentCulture.name);

 178: 

 179:                         var encodedChangedBySnippet = String.format(

 180:                             $htmlEncode(Srch.Res.hp_ChangedByAuthorDate),

 181:                             htmlUserPresence,

 182:                             $htmlEncode(changedDate),

 183:                             $htmlEncode(changedTime));

 184: 

 185:                         var targetElement = document.getElementById(changedById);

 186:                         if(targetElement)

 187:                         {

 188:                             targetElement.innerHTML = encodedChangedBySnippet;

 189:                         }

 190: 

 191:                         ProcessImn();

 192:                     });

 193:                 });

 194:             });

 195: _#-->

 196:             <div class="ms-srch-hover-subTitle ms-srch-hover-text ms-srch-ellipsis" id="_#= $htmlEncode(changedById) =#_"></div>

 197: <!--#_

 198:             dataShown = true;

 199:             changedBySnippetShown = true;

 200:         }

 201: 

 202:         if(ctx.currentItem_ShowChangedBySnippet &&

 203:            !changedBySnippetShown &&

 204:            !ctx.currentItem_NeverFallbackToAuthor)

 205:         {

 206:             ctx.CurrentItem.csr_ShowLastModifiedTime = true;

 207:             ctx.CurrentItem.csr_ShowAuthors = true;

 208:         }

 209: 

 210:         if (!Srch.U.n(ctx.CurrentItem.LastModifiedTime) &&

 211:             ctx.CurrentItem.csr_ShowLastModifiedTime)

 212:         {

 213:             var lastModifiedTimeId = id + HP.ids.modifiedDate;

 214:             AddPostRenderCallback(ctx, function()

 215:             {

 216:                 Srch.U.renderFriendlyTimeIntervalString(ctx.CurrentItem.LastModifiedTime, lastModifiedTimeId);

 217:             });

 218: 

 219: _#-->

 220:             <div class="ms-srch-hover-subTitle"><h3 class="ms-soften">_#= $htmlEncode(Srch.Res.hp_LastModified) =#_</h3></div>

 221:             <div class="ms-srch-hover-text ms-srch-ellipsis" id="_#= $htmlEncode(lastModifiedTimeId) =#_"></div>

 222: <!--#_

 223:             dataShown = true;

 224:         }

 225: 

 226:         if (!$isEmptyString(ctx.CurrentItem.DisplayAuthor) &&

 227:             ctx.CurrentItem.csr_ShowAuthors)

 228:         {

 229:             var authorsLabel = $resource("hp_RecentlyEdited");

 230:             if(!$isEmptyString(ctx.CurrentItem.csr_AuthorsLabel))

 231:             {

 232:                 authorsLabel = ctx.CurrentItem.csr_AuthorsLabel;

 233:             }

 234: 

 235:             var authors = Srch.U.getArray(ctx.CurrentItem.DisplayAuthor);

 236: _#-->

 237:             <div class="ms-srch-hover-subTitle"><h3 class="ms-soften">_#= $htmlEncode(authorsLabel) =#_</h3></div>

 238:                 <div class="ms-srch-hover-text">

 239:                     _#= HP.GetAuthorsHtml(id, authors) =#_

 240:                 </div>

 241: <!--#_

 242:             dataShown = true;

 243:         }

 244: 

 245:         if(!ctx.CurrentItem.csr_DataShown && !dataShown)

 246:         {

 247: _#-->

 248:             <div class="ms-srch-hover-noContent" id="_#= $htmlEncode(id + HP.ids.noData) =#_">_#= $htmlEncode(Srch.Res.hp_NoData) =#_</div>

 249: <!--#_

 250:         }

 251: _#-->

 252:                 </div>            

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

 254:                     <!-- _#= ctx.RenderFooter(ctx) =#_ -->

 255:                     <!--#_    

 256:         var id = ctx.CurrentItem.id;   

 257:         var appAttribs = "";

 258:         if (!$isEmptyString(ctx.CurrentItem.csr_OpenApp)) { appAttribs += "openApp=\"" + $htmlEncode(ctx.CurrentItem.csr_OpenApp) + "\"" }; 

 259:         if (!$isEmptyString(ctx.CurrentItem.csr_OpenControl)) { appAttribs += " openControl=\"" + $htmlEncode(ctx.CurrentItem.csr_OpenControl) + "\"" };

 260: 

 261:         if (!Srch.U.e(ctx.CurrentItem.Path))

 262:         {

 263:             var editId = id + HP.ids.open;

 264:             var editTitle = Srch.Res.hp_Tooltip_Open;

 265:             var editText = Srch.Res.hp_Open;

 266: 

 267:             if (ctx.currentItem_IsOfficeDocument)

 268:             {

 269:                 editId = id + HP.ids.open;

 270:                 editTitle = Srch.Res.hp_Edit;

 271:                 editText = Srch.Res.hp_Edit;

 272:             }

 273:             else if (!Srch.U.e(ctx.CurrentItem.ServerRedirectedURL) && !ctx.CurrentItem.IsContainer)

 274:             {

 275:                 editId = id + HP.ids.openClient;

 276:                 editTitle = Srch.Res.hp_Tooltip_OpenInClient;

 277:                 editText = Srch.Res.hp_Edit;

 278:             }

 279: 

 280:             var editHmtl = String.format('<a clicktype="ActionEdit" id="{0}" class="ms-calloutLink ms-uppercase" href="{1}" title="{2}" {3}>{4}</a>', 

 281:                                             $htmlEncode(editId), $urlHtmlEncode(ctx.CurrentItem.Path), $htmlEncode(editTitle), appAttribs, $htmlEncode(editText));

 282: _#-->                

 283:             <div class="ms-srch-hover-action">        

 284:                 _#= editHmtl =#_                   

 285:             </div>

 286: <!--#_

 287:         }

 288:         if (!Srch.U.isSPFSKU() && ctx.CurrentItem.csr_ShowFollowLink && !$isEmptyString(ctx.CurrentItem.Path)){

 289:             var isDoc = true;

 290:             if(!$isNull(ctx.CurrentItem.csr_IsSite) && ctx.CurrentItem.csr_IsSite == true){

 291:                 isDoc = false;

 292:             }

 293: _#-->

 294:             <div class="ms-srch-hover-action">

 295:                 <a id="_#= $htmlEncode(id + HP.ids.follow) =#_" class="ms-calloutLink ms-uppercase" href="javascript:HP.Follow('_#= $scriptEncode(ctx.CurrentItem.Path) =#_', true, _#= isDoc =#_)" title="_#= $htmlEncode(Srch.Res.hp_Tooltip_Follow) =#_">

 296:                     _#= $htmlEncode(Srch.Res.hp_Follow) =#_

 297:                 </a>

 298:             </div>

 299: <!--#_

 300:         }

 301: _#-->

 302:         <div class="ms-srch-hover-action">

 303: <!--#_

 304:             var emailLink = HP.GetEmailLink(ctx.CurrentItem.Title, ctx.CurrentItem.Path, ctx.CurrentItem.csr_ClientType, ctx.CurrentItem.ServerRedirectedURL);

 305: _#-->

 306:             <a clicktype="ActionSend" id="_#= $htmlEncode(id + HP.ids.send) =#_" class="ms-calloutLink ms-uppercase" title="_#= $htmlEncode(Srch.Res.hp_Tooltip_Send) =#_" href="_#= $htmlEncode(emailLink) =#_">

 307:                 _#= $htmlEncode(Srch.Res.hp_Send) =#_

 308:             </a>

 309:         </div>

 310: <!--#_

 311:         if(!Srch.U.e(ctx.CurrentItem.ParentLink) && ctx.CurrentItem.csr_ShowViewLibrary) {

 312: _#-->

 313:             <div class="ms-srch-hover-action">

 314:                 <a clicktype="ActionViewLibrary" id="_#= $htmlEncode(id + HP.ids.parentLink) =#_" class="ms-calloutLink ms-uppercase" title="_#= $htmlEncode(Srch.Res.hp_Tooltip_ViewLibrary) =#_" href="_#= $urlHtmlEncode(ctx.CurrentItem.ParentLink) =#_">_#= $htmlEncode(Srch.Res.hp_ViewLibrary) =#_</a>

 315:             </div>

 316: <!--#_

 317:         }

 318:         if(ctx.ClientControl.get_showViewDuplicates() && !Srch.U.n(ctx.CurrentItem.CollapsingStatus) && !Srch.U.n(ctx.CurrentItem.DocId)) {

 319:             if(ctx.CurrentItem.CollapsingStatus === 1){

 320: _#-->

 321:                 <div class="ms-srch-hover-action">

 322:                     <a clicktype="ActionViewDupes" id="_#= $htmlEncode(id + HP.ids.viewDuplicates) =#_" class="ms-calloutLink ms-uppercase" title="_#= $htmlEncode(Srch.Res.hp_Tooltip_ViewDuplicates) =#_" href="javascript:HP.ViewDuplicates('_#= $scriptEncode(ctx.CurrentItem.id) =#_',_#= ctx.CurrentItem.DocId =#_)">

 323:                         _#= $htmlEncode(Srch.Res.hp_ViewDuplicates) =#_

 324:                     </a>

 325:                 </div>

 326: <!--#_

 327:             }

 328:         }

 329: _#-->

 330:                 </div>

 331:             </div> 

 332:         </div>

 333:     </div>

 334: </body>

 335: </html>

Now you have a full fledged hover panel display template! Upload it to the master page gallery and you should be able to reference it from your item display templates by changing the JavaScript in the your item Display Template.

   1: var hoverUrl = "~sitecollection/_catalogs/masterpage/Display Templates/Search/Item_Modified_HoverPanel.js";

Full Item Default Display Template Code:

   1: <html xmlns:mso="urn:schemas-microsoft-com:office:office" xmlns:msdt="uuid:C2F41010-65B3-11d1-A29F-00AA00C14882"> 

   2: <head>

   3: <title>Modified Item Body</title>

   4:  

   5: <!--[if gte mso 9]><xml>

   6: <mso:CustomDocumentProperties>

   7: <mso:TemplateHidden msdt:dt="string">0</mso:TemplateHidden>

   8: <mso:MasterPageDescription msdt:dt="string">Modified Item Display Template</mso:MasterPageDescription>

   9: <mso:ContentTypeId msdt:dt="string">0x0101002039C03B61C64EC4A04F5361F385106603</mso:ContentTypeId>

  10: <mso:TargetControlType msdt:dt="string">;#SearchResults;#</mso:TargetControlType>

  11: <mso:HtmlDesignAssociated msdt:dt="string">1</mso:HtmlDesignAssociated>

  12: <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'</mso:ManagedPropertyMapping>

  13: </mso:CustomDocumentProperties>

  14: </xml><![endif]-->

  15: </head>

  16: <body>

  17:     <div id="Item_CommonModified_Body">

  18: <!--#_

  19:         var id = ctx.CurrentItem.csr_id;

  20:         var title = Srch.U.getHighlightedProperty(id, ctx.CurrentItem, "Title");

  21:         if ($isEmptyString(title)) {title = $htmlEncode(ctx.CurrentItem.Title)}

  22: 

  23:         var useWACUrl = !$isEmptyString(ctx.CurrentItem.ServerRedirectedURL);

  24:         if(ctx.ScriptApplicationManager && ctx.ScriptApplicationManager.states){

  25:             useWACUrl = (useWACUrl && !ctx.ScriptApplicationManager.states.openDocumentsInClient);

  26:         }

  27: 

  28:         var appAttribs = "";

  29:         if(!useWACUrl)

  30:         {

  31:             if (!$isEmptyString(ctx.CurrentItem.csr_OpenApp)) { appAttribs += "openApp=\"" + $htmlEncode(ctx.CurrentItem.csr_OpenApp) + "\"" }; 

  32:             if (!$isEmptyString(ctx.CurrentItem.csr_OpenControl)) { appAttribs += " openControl=\"" + $htmlEncode(ctx.CurrentItem.csr_OpenControl) + "\"" };

  33:         }

  34: 

  35:         var showHoverPanelCallback = ctx.currentItem_ShowHoverPanelCallback;

  36:         if (Srch.U.n(showHoverPanelCallback)) {

  37:             var itemId = id + Srch.U.Ids.item;

  38:             var hoverId = id + Srch.U.Ids.hover;

  39:             var hoverUrl = "~sitecollection/_catalogs/masterpage/Display Templates/Search/Item_Modified_HoverPanel.js";

  40:             showHoverPanelCallback = Srch.U.getShowHoverPanelCallback(itemId, hoverId, hoverUrl);

  41:         }

  42:         var displayPath = Srch.U.getHighlightedProperty(id, ctx.CurrentItem, "Path");

  43:         if ($isEmptyString(displayPath)) {displayPath = $htmlEncode(ctx.CurrentItem.Path)} 

  44:         var url = ctx.CurrentItem.csr_Path;

  45:         if($isEmptyString(url)){

  46:             if(useWACUrl)

  47:             {

  48:                 url = ctx.CurrentItem.ServerRedirectedURL;

  49:             } else {

  50:                 url = ctx.CurrentItem.Path;

  51:             }        

  52:         }

  53:         ctx.CurrentItem.csr_Path = url;

  54:         var pathLength = ctx.CurrentItem.csr_PathLength;

  55:         if(!pathLength) {pathLength = Srch.U.pathTruncationLength}

  56: 

  57:         var maxTitleLengthInChars = Srch.U.titleTruncationLength;

  58:         var termsToUse = 2;

  59:         if(ctx.CurrentItem.csr_PreviewImage != null)

  60:         {

  61:             maxTitleLengthInChars = Srch.U.titleTruncationLengthWithPreview;

  62:             termsToUse = 1;

  63:         }

  64: 

  65:         var clickType = ctx.CurrentItem.csr_ClickType;

  66:         if(!clickType) {clickType = "Result"}        

  67: _#-->                

  68:         <div id="_#= $htmlEncode(id + Srch.U.Ids.body) =#_" class="ms-srch-item-body" onclick="_#= showHoverPanelCallback =#_">

  69: <!--#_

  70:             if (!$isEmptyString(ctx.CurrentItem.csr_Icon)) {

  71: _#-->

  72:                 <div class="ms-srch-item-icon">    

  73:                     <img id="_#= $htmlEncode(id + Srch.U.Ids.icon) =#_" onload="this.style.display='inline'" src="_#= $urlHtmlEncode(ctx.CurrentItem.csr_Icon) =#_" />

  74:                 </div>

  75: <!--#_

  76:             }

  77:             var titleHtml = String.format('<a clicktype="{0}" id="{1}" href="{2}" class="ms-srch-item-link" title="{3}" onfocus="{4}" {5}>{6}</a>',

  78:                                           $htmlEncode(clickType), $htmlEncode(id + Srch.U.Ids.titleLink), $urlHtmlEncode(url), $htmlEncode(ctx.CurrentItem.Title), 

  79:                                           showHoverPanelCallback, appAttribs, Srch.U.trimTitle(title, maxTitleLengthInChars, termsToUse));

  80: _#-->       

  81:             <div id="_#= $htmlEncode(id + Srch.U.Ids.title) =#_" class="ms-srch-item-title"> 

  82:                 <h3 class="ms-srch-ellipsis">

  83:                     _#= titleHtml =#_

  84:                 </h3>

  85:             </div>

  86: <!--#_ 

  87:             if (!$isEmptyString(ctx.CurrentItem.HitHighlightedSummary)) { 

  88: _#-->

  89:                 <div id="_#= $htmlEncode(id + Srch.U.Ids.summary) =#_" class="ms-srch-item-summary">_#= Srch.U.processHHXML(ctx.CurrentItem.HitHighlightedSummary) =#_</div>

  90: <!--#_ 

  91:             }

  92:             var truncatedUrl = Srch.U.truncateHighlightedUrl(displayPath, pathLength);

  93: _#-->

  94:             <div id="_#= $htmlEncode(id + Srch.U.Ids.path) =#_" tabindex="0" class="ms-srch-item-path" title="_#= $htmlEncode(ctx.CurrentItem.Path) =#_" onblur="Srch.U.restorePath(this, '_#= $scriptEncode(truncatedUrl) =#_', '_#= $scriptEncode(ctx.CurrentItem.Path) =#_')" onclick="Srch.U.selectPath('_#= $scriptEncode(ctx.CurrentItem.Path) =#_', this)" onkeydown="Srch.U.setPath(event, this, '_#= $scriptEncode(ctx.CurrentItem.Path) =#_', '_#= $scriptEncode(truncatedUrl) =#_')" >

  95:                 _#= truncatedUrl =#_

  96:             </div>

  97:         </div>

  98: <!--#_

  99:         if (!$isEmptyString(ctx.CurrentItem.csr_PreviewImage)) 

 100:         {

 101:             var altText = Srch.Res.item_Alt_Preview;

 102:             if(!$isEmptyString(ctx.CurrentItem.csr_PreviewImageAltText)){

 103:                 altText = ctx.CurrentItem.csr_PreviewImageAltText;

 104:             }

 105: 

 106:             var onloadJS = "var container = $get('" + $scriptEncode(id + Srch.U.Ids.preview) + "'); if(container){container.style.display = 'inline-block';}" +

 107:                            "var path = $get('" + $scriptEncode(id + Srch.U.Ids.path) + "'); if (path) { Srch.U.ensureCSSClassNameExist(path, 'ms-srch-item-preview-path');}" +

 108:                            "var body = $get('" + $scriptEncode(id + Srch.U.Ids.body) + "'); if (body) { Srch.U.ensureCSSClassNameExist(body, 'ms-srch-item-summaryPreview');}";

 109: 

 110:             var previewHtml = String.format('<a clicktype="{0}" href="{1}" class="ms-srch-item-previewLink" {2}><img class="ms-srch-item-preview" src="{3}" alt="{4}" onload="{5}" /></a>', 

 111:                                             $htmlEncode(clickType), $urlHtmlEncode(url), appAttribs, $urlHtmlEncode(ctx.CurrentItem.csr_PreviewImage), $htmlEncode(altText), onloadJS);

 112: _#-->       

 113:             <div id="_#= $htmlEncode(id + Srch.U.Ids.preview) =#_"  class="ms-srch-item-previewContainer"> 

 114:                 _#= previewHtml =#_

 115:             </div>

 116: <!--#_

 117:         }

 118: _#-->

 119:     </div>

 120: </body>

 121: </html>

]]>
https://blogs.perficient.com/2013/02/21/sp2013-search-dyi-display-template/feed/ 0 224269
SP2013 Search Display Templates Semi-Deep Dive https://blogs.perficient.com/2013/02/19/sp2013-search-display-templates-semi-deep-dive/ https://blogs.perficient.com/2013/02/19/sp2013-search-display-templates-semi-deep-dive/#comments Tue, 19 Feb 2013 20:36:54 +0000 https://blogs.perficient.com/microsoft/?p=17229

If you’ve stumbled around display templates in SharePoint 2013 Search, you’ve probably run into these following JavaScript do-dads:

  • ctx.RenderBody(ctx)  -> (standard search results Display Template)
  • ctx.RenderBody(ctx)   -> (hover panel Display Template)
  • ctx.RenderFooter(ctx)  -> (hover panel Display Template)
  • ctx.RenderHeader(ctx) –> (hover panel Display Template)

You can find these methods on items such as Item_Default.html, Item_PDF.html, Item_Word.html, and their corresponding hover panel Display Templates.
So what exactly is being rendered? For this we need to take a look at the following files. You can find these at Site Settings –> Master Pages and Page Layouts –> Display Templates –> Search

  • ctx.RenderBody(ctx) –> Item_CommonItem_Body.html    (standard search results Display Template)
  • ctx.RenderHeader(ctx) –> Item_CommonHoverPanel_Header.html    (hover panel Display Template)
  • ctx.RenderBody(ctx) -> Item_CommonHoverPanel_Body.html    (hover panel Display Template)
  • ctx.RenderFooter(ctx) -> Item_CommonHoverPanel_Actions.html    (hover panel Display Template)

 

So what does this mean exactly?

If you modify Item_CommonItem_Body.html, you will change the look and feel of any Display Template that calls ctx.RenderBody(ctx). The same idea goes for the other xxxx_CommonItem_xxxx Display Templates. If you don’t feel that OPEN, SEND, and VIEW LIBRARY are clear enough for your user, you can change the html in Item_CommonHoverPanel_Actions.html to whatever you want or add links of your own.
So if you need to change the universal look and feel of your items, look at modifying the CommonItem html files. While I’m not a fan of modifying a crucial OOTB file, certain platforms such as SharePoint 2013 Online might not have any options. For on-premise SharePoint 2013, I’m still looking for an option somewhere such as PowerShell to change the default Item_CommonItem_Body file to render.
Stay tuned for an upcoming blog on how to glue together your own custom Item Display Template so you can show your custom result types.
 
Some visuals:

Result Display Rendering

item1

Hover Panel Display Rendering

item2

]]>
https://blogs.perficient.com/2013/02/19/sp2013-search-display-templates-semi-deep-dive/feed/ 6 224267
SP2013 Search Quickie – Query Rule By Folder https://blogs.perficient.com/2012/12/13/sp2013-search-quickie-query-rule-by-folder/ https://blogs.perficient.com/2012/12/13/sp2013-search-quickie-query-rule-by-folder/#respond Thu, 13 Dec 2012 17:26:46 +0000 https://blogs.perficient.com/microsoft/?p=10519

Quick and dirty, without any manipulation of managed properties. This can be handy for file share crawling where SharePoint will index the physical folders of the file share.

To get to your query rules, go to Site Settings –> Query Rules (Under Search).

Configure a rule for Local SharePoint Results:

2012-12-13_1114

Click “New Query Rule”:

– Create a Name for the rule, Folders seems sufficient

– Set Query Conditions to “Query Contains Action Term” –> Action Terms being “Folder; Folders” (semi-colon delimited)

2012-12-13_1115

 

Click “Add Result Block” and on the next screen “Launch Query Builder”:

Set Select a query to the “Query’s original source”

– Keyword filter to {subjectTerms} –> Add that to the Query text

– Property Filter: Select IsContainer from the managed properties, set the Manual value to true –> Add that property filter

From here you can test your query to make sure that it returns what you’re looking for. In this case, folders.

2012-12-13_1116

 

Save your work, and folders should now show up as a promoted result block for your users!

]]>
https://blogs.perficient.com/2012/12/13/sp2013-search-quickie-query-rule-by-folder/feed/ 0 224215