in

Dot Net Mafia

Group site for developer blogs dealing with (usually) .NET, SharePoint 2013, SharePoint 2010, Office 365, SharePoint Online, and other Microsoft products, as well as some discussion of general programming related concepts.

This Blog

Syndication

Archives

Corey Roth [MVP]

A SharePoint MVP bringing you the latest time saving tips for SharePoint 2013, Office 365 / SharePoint Online and Visual Studio 2013.
  • How to: Use the SharePoint 2013 Content Enrichment Web Service

    The Content Enrichment Web Service (CEWS) allows you to extend the functionality of SharePoint 2013 Search.  Using CEWS, a developer can send the values of managed properties to an external web service and return new or modified managed properties to include in the index.  The process involved implementing a custom WCF service and then registering it with PowerShell.  The PowerShell cmdlet specifies which properties go into and out of the service.

    This post has been cross-posted to MSDN Code where you can download a working sample and deploy it.

    This example will take the values of the Author and LastModifiedTime managed properties and write a new string such as "Modified by <author> on <LastModifiedTime>." to the managed property TestProperty.  This property need to be created first prior to trying to use your Content Enrichment Web Service.  The property should be configured as type Text with the following attributes: Query, Search,Retrieve, and Refine.

    CEWSNewManagedProperty

    To get started, create a new WCF Service Project called ContentEnrichmentExampleService.

    CEWSVisualStudioNewService

    Once the project is created, you can delete the default service Service1.svc and IService.cs as it won't be needed.

    Next, you will need to add a reference to the following assembly. 

    • microsoft.office.server.search.contentprocessingenrichment.dll

    This assembly can be found in the folder Installation Path\Microsoft Office Servers\15.0\Search\Applications\External.

    Now, we need to create the service to do the content enrichment processing.  Create a new service called ContentEnrichmentExampleService.svc. 

    CEWSVisualStudioNewService2

    Delete the file IContentEnrichmentExampleService.cs as it will not be needed.  The custom service instead inherits from IContentProcessingEnrichmentService.

    Now we can start adding our code to ContentEnrichmentProcessingExampleService.svc.cs.  This code will retrieve the values from the input properties, create our new output property TestProperty and send it back to the search index.

    Start by adding using statements to the assembly we added.

    using Microsoft.Office.Server.Search.ContentProcessingEnrichment;

    using Microsoft.Office.Server.Search.ContentProcessingEnrichment.PropertyTypes;

    The interface that the class is inheriting from will be shown as broken since you deleted it.  Change it instead to inherit from IContentProcessingEnrichmentService.

    public class ContentEnrichmentExampleService : IContentProcessingEnrichmentService

    Add a ProcessedItem collection to hold the output managed property values from the service.

    private readonly ProcessedItem processedItemHolder = new ProcessedItem

    {

        ItemProperties = new List<AbstractProperty>()

    };

    Then, Implement the ProcessItem method.  This method receives the input managed properties and allows you to write code to generate the output managed properties.

    public ProcessedItem ProcessItem(Item item)

    {

     

    }

    Inside the ProcessItem method, initialize the ErrorCode and ItemProperties.

    processedItemHolder.ErrorCode = 0;

    processedItemHolder.ItemProperties.Clear();

    We then, need to Create a new output managed property named TestProperty.  The property object takes types based on what type of managed property you defined.

    var testProperty = new Property<string>();

    testProperty.Name = "TestProperty";

    Now we are going to retrieve the managed properties using a simple lamdba expression.  Remember that the names of properties are case sensitive and need to match exactly how it shows on the Search Schema page.  You also need to cast the object to the appropriate type.  Since the Author managed property is a multi-valued property, we need to use List<string>.  The LastModifiedTime is a date so we use a DateTime type.

    var authorProperty = item.ItemProperties.FirstOrDefault(i => i.Name == "Author") as Property<List<string>>;

    var writeProperty = item.ItemProperties.FirstOrDefault(i => i.Name == "LastModifiedTime") as Property<DateTime>;

    Now, we need to verify that the properties aren't null.

    if ((authorProperty != null) && (writeProperty != null))

    {

     

    }

    We are then going to write out a new string to TestProperty in the format Modified by {Author} on {LastModifiedTime}.  Since Author supports multiple values, only the first value was used.  This value goes in the Value property.  Once we set the value, we have to add it processedItemHolder so that it can send the values back to the search index.

    testProperty.Value = string.Format("Modified by {0} on {1}.", authorProperty.Value.First(), writeProperty.Value);

    processedItemHolder.ItemProperties.Add(testProperty);

    Return the processItemHolder

    return processedItemHolder;

    At this point, we can run and debug our service using F5.  Leave the service running as it will be called when doing a full crawl.

    To register the service with SharePoint we use using the New-SPEnterpriseSearchContentEnrichmentConfiguration cmdlet.  Use the following PowerShell script  to register the Content Enrichment Web Service.  Verify that the Endpoint parameter contains the correct URL to your service.  The example below has the location used in the source code I provided.  If you start from scratch or you have deployed you service to a remote server, then you will need to update the address.

    $ssa = Get-SPEnterpriseSearchServiceApplication  $config = New-SPEnterpriseSearchContentEnrichmentConfiguration  $config.Endpoint = "http://localhost:54641/ContentEnrichmentExampleService.svc"  $config.InputProperties = "Author", "LastModifiedTime"  $config.OutputProperties = "TestProperty"  $config.SendRawData = $false  $config.Timeout = 30000  $config  Set-SPEnterpriseSearchContentEnrichmentConfiguration –SearchApplication $ssa –ContentEnrichmentConfiguration $config

    The InputProperties parameter specifies the managed properties sent to the service.  The OutputProperties specifies the managed properties returned by the service.  Note, that both are case sensitive.  All managed properties referenced need to be created in advance.  Set the Timeout propety higher to give yourself sufficient time to debug.  For a complete reference on parameters, see this MSDN reference.

    After registering your content enrichment service, start a full crawl.  Again, ensure that your Content Enrichment Web Service is running in the debugger.  While it is crawling, you can set breakpoints as desired. 

    To verify the functionality after the crawl is complete, issue a query using REST in the browser like the one below.

    http://server/_api/search/query?querytext='*'&selectproperties='title,path,author,testproperty'

    This query will return every item in the index and include the new TestProperty field.  You can verify that the new property was included and has the expected result as shown in the example below.

    CEWSRESTAPIQuery

    I hope this gets you started with Content Enrichment Web Services.  I have a few follow-up posts to include on some more of the PowerShell parameters, but I hope this helps.

    Again, you can find the complete source code and PowerShell script on MSDN Code.  Feel free to leave me a comment if you run into an issue or have a question.

  • Office for iPad: Good new for Office 365, bad news for Surface RT

    If you saw one of the many tweets yesterday, you know that Microsoft announced Office has been released for iPad.  I'm sure this pleases a lot of iPad owners.   For those without Office 365 subscriptions though, you will soon realize that you only have access to read and view documents.  To make edits with an iPad and Office 365 subscription is required.  I am sure a lot of you out there will be disappointed by this since most companies still operate on-premises.

    I think this is absolutely great for adoption of Office 365 though.  Think about it.  When it comes to organizations, your executives are often carrying iPads.  They will find out about Office and download it and will quickly realize they can't make any edits.  I feel sorry for the CIO that has to report to his board why they can't edit documents.  I can just hear the CEO saying, "Well, why aren't we in the cloud?".  Sure they can purchase individual subscriptions to Office 365 Home, but that doesn't make a lot of sense for the Enterprise.  I'm not saying this is the tipping point for organizations to jump to the cloud, but it's just one more thing pushing companies there.

    Bad news for Surface RT

    Today Surface RT just died a little and that makes me sad.  When I am traveling, I get asked all the time about my Surface 2.  People are astonished that I can run Office and Skype at the same time.  While iPad still sucks at multi-tasking as shown in the latest Samsung commercial, running Office was a competitive advantage of the Surface 2.  Now, the Office 365 requirement will set some people back another $99 / year or require your company to be in the cloud, it really makes things even harder on the Windows RT platform.  I still believe in it strongly, but it's a set back.  I've often laughed at people trying to use iPads for work.  Now they might actually be able to do some if they can find a third party after market that works ok.  When Jeff Teper, said they really want you to be able to use Office anywhere, I get it.  I just am sad the Surface RT advantage got a little smaller.

  • If SharePoint forms are a priority to your business, then maybe Office 365 should be too

    There has been a lot of talk about forms in the last year.  Your business users need it and your developers want it too.  The current version of SharePoint doesn't offer much in this space right now.  At SharePoint Conference 2014, Microsoft announced the forms roadmap for SharePoint.  Since at least last year, Microsoft has been telling you that SharePoint is now a "service-first" offering.  That means if you want the newest features, you need to be in Office 365.  Excel Surveys was one of the first examples of this.  If you don't mind waiting three years to get something new, by all means stay on-premises. 

    That being said, we're going to start seeing iterative releases for forms technology based on Access showing up as soon as this summer in Office 365.  This is the Forms on SharePoint Lists "FoSL" technology mentioned in the session.  Microsoft is focusing on an iterative process for continuous improvement here.  That means, we'll get a simplified version of the forms technology soon that includes some of the following features (mentioned in SPC348):

    • Auto form layout based on list schema
    • Lookup between lists
    • Cascading combo boxes
    • SharePoint list workflows

    This tool is designed around the information worker so that they can easily build their forms around SharePoint lists.  What is particularly exciting is that it supports workflows.  This was a key reason that information workers couldn't build business solutions using Access Services 2013.  Cascading combo boxes have always been particularly troublesome in the past as well.  All of these features are subject to change, but these are some great features targeted for launch.  Will it have everything you need?  Probably not, but they have more features targeted such as hiding / showing sections, profile information, and business logic coming in the next year.  Want to have a say what is going into the product?  Vote for it on User Voice.  Here you can suggest ideas and vote for the features that matter most to you.

    I've had this forms discussion with a lot of clients in the last year.  It's been a hard conversation, because there really was no good answer.  For clients that just needed a handful of forms built, many of them found it was cheaper and more effective to just engage a developer.  For clients that wanted to empower end users, the discussion usually turned to third party tools.  Both Nintex and K2 make some great forms products, but they both tend to be highly cost prohibitive.  Even some of my clients with the largest budgets, just couldn't pull the trigger due to cost.  If you have the money, these third party tools can probably meet your immediate needs.  However, if you can wait a bit longer, maybe it makes sense to put those dollars towards migration to Office 365.  By the time you are done, Microsoft will more than likely will have a forms project that can meet a lot of your needs.  That being said, the third party tools are more mature products at this point.  They will offer more features to you now, than FoSL will at launch, so you really have to weight in what makes sense for your business. 

    Now, I could be totally wrong on this since all I have seen is the same demo many of you have at SharePoint Conference.  Many ISVs have built good businesses around the gaps in SharePoint.  They can charge a premium for a feature you need now.  However, those gaps tend to get filled over time.  Social is a great example.  Back in 2011, lots of companies were considering NewsGator.  Many opted out, again due to cost.  Then Microsoft bought Yammer and now the company formerly known as NewsGator is now known as Sitrion and is focusing on HR solutions.  I'm not saying that I see K2 or Nintex going out of business any time soon, but I hope they are working on something great to fill that next gap.  While you may argue that the Microsoft forms solution may never be as mature as what the ISVs offer, it may be good enough.  Customers will opt to get by with what it provides rather than sign a PO with an ISV to get more.

    Now is forms the tipping point to get you to make the jump to the cloud?  Maybe not, but this is only the first of many things that you will see online years before it gets to on-premises.  Think about it, the next version of SharePoint on-premises isn't coming until the end of next year.  That means SharePoint Online users will have access to the forms technology a year or more before on-premises users.  That's assuming you migrated to SharePoint vNExt as soon as it came out too.  It's hard to shift from a reactive to a pro-active IT organization when you have to tell your users sorry, you need to wait two more years for that.

    As I said at the end of last year, 2014 is the year of the hybrid.  You'll see more organizations this putting one foot in the cloud.  Whether they start with OneDrive for Business or they go all in, you'll see new organizations looking at the cloud this year when previously they swore they would never go.  Never say never.  When companies look at spending 500k, 1 million, or even more on doing an upgrade every couple of years, you have to ask yourself why?  Wouldn't you rather just do one last migration to SharePoint Online and then never have to worry about it again?  If I was a CIO, that's certainly where I would be looking.  Of course, moving to the cloud has it caveats, concerns, downtime, performance issues, and risks.  I understand that it's not right for everyone.  However, if Office 365 is viable for your business though, why wouldn't you do it and never have to worry about another migration again?

    After the forms session, I had a conversation on SPCtv with Greg Lindhorst, Sonya Koptyev, and Karuana about the future of forms.  Be sure and watch it for more information.

    SPCtv Insights into the Future of Forms

    Follow me on twitter: @coreyroth

  • A look at the new simplified Yammer login with Office 365

    The path to SSO between Office 365 and Yammer is well underway with the new simplified login feature in Office 365.  Merging two separate identity systems is no easy task, but Microsoft is one step closer with this new level of integration.  What does this mean exactly?  It means when you are on an Office 365 site, and you click the Yammer link at the top, you will be logged directly into Yammer without being prompted for credentials.

    In this example, I logged in as my test user Garth Fort, clicked the Yammer link and after a series of redirects through Yammer.aspx on the My Sites host and a number of others, it logged me into Yammer with this same user account.  This is even with me never having logged into Yammer before with this user.

    SPOYammerAutologin

    That's pretty awesome.  To an end user, that might seem insignificant because you just expect something like that to just work.  However, there had to be a ton of work behind the scenes to make that happen.  Now, as Christophe (@cfiessinger) mentions should I go directly to Yammer in a fresh browser, it's going to require a login.  If I go to my Office 365 site after logging into Yammer, that will require authentication as well.  However, this is a significant step in the right direction.

    I'll also add after I re-enabled Yammer, that I had to log out and close my browser once before any of the integration started to work.

    This got me to wondering though, what happens if I use a brand new account that hasn't logged into Office 365 or Yammer before?  I created a new account in the Office 365 portal and assigned a user license to give it a try.  When you click on the Yammer link, it does, what we would expect, it prompts you to create a new account in Yammer.

    SPOYammerNewUser

    I filled in the information, but I purposely chose a different password than what my Office 365 user account had.  I then closed the browser and started a new In-Private session.  When I tried the Yammer link again, it in-fact logged me straight into Yammer despite my Office 365 and Yammer account having different passwords.

    Again, to an end user this seems obvious, but a lot has to happen in the background to make this happen.  As a reminder, you can only get this functionality right now if you disable and re-enable Yammer in your Office 365 environment.  If you are going to SharePoint Conference (#SPC14), I expect you can learn more about this functionality.

  • New SharePoint Online Tenants feature OneDrive branding

    After Service Pack 1 went live this week, I decided to provision a new Office 365 tenant to see if the OneDrive changes are present there as well.  It turns out that they are.  When you login, you'll see OneDrive at the top.  I really wish this would say OneDrive for Business to avoid confusion with the consumer offering.  However, I think this is a screen real estate issue.

    SPOHomePageOneDrive

    Tenant administration has the naming right.

    SPOOneDriveForBusinessTenantAdmin

    Clicking on the OneDrive link, looks similar to the old SkyDrive Pro experience.

    SPOOneDriveForBusiness

    My existing tenants still say SkyDrive.  I suspect they will stay that way for quite a while, but maybe we'll hear more at SharePoint Conference.  If you happen to hear anything on the timeline of this during a session, please let me know with a comment or tweet! 

  • My new travel blog is live!

    I mentioned last week that I was starting a travel blog.  With SharePoint Conference (#SPC14) just around the corner, I thought this was the perfect time to launch it.  With people coming from all over the world, I thought it would be a great place to get people's perspectives on travel. 

    Why a travel blog?  Well like many of you, I have traveled a lot over the years.  As some of you know, I am a huge @SouthwestAir fan and so many of my posts will center around making the most of your experience there.  I truly believe they are the best airline out there, but the open seating policy scares a lot of you all away.  Read some of my posts and maybe you just might change your mind.  Even for the most seasoned of travelers, you may pick up a tip or two as well.

    Will this blog only be about Southwest?  No, but it will be featured often.  I'll post on everything from rental cars to airport survival guides.

    Today, I have released my first three posts:

    I'm excited about this launch and SouthwestAir even gave me a retweet to their 1.6 Million followers so traffic is coming in strong already.

    At SPC, I'll be asking some of you about your experiences on the way over if you don't mind sharing.  Can't wait to see you all there!

    I am keeping this blog technical, so that's the last you will hear of it here.  You will see me post on twitter from time to time about it.

  • Useful JavaScript to know when working with SharePoint Display Templates (#SPC3000)

    With the display templates feature in SharePoint 2013, you can highly customize the look of search using HTML, JavaScript, and jQuery.  It turns out there are a lot of helper functions to make your life easier in the out-of-the-box scripts.  Unfortunately, there is not a lot of documentation out there on how to get started aside from looking at the code of OOTB display templates.  You can also find quite a bit by looking at the debug versions of the scripts Search.cbs.debug.js and Search.clientcontrols.js.  This post today serves as a guide to go along with my upcoming Display Templates talk (#SPC3000) at SharePoint Conference 2014.

    Meet ctx

    When it comes to display templates, ctx is your content object for your display template.  With this object, you can get access to managed properties, the number of results, and DOM elements that make up your display template.  Your display template will have a reference to ctx automatically and you will need to pass it to many of the methods to get access.

    Accessing the Current Item

    Now that you have met ctx, you can use it to do a number of actions.  Use this in your item display templates and hover panels.

    ctx.CurrentItem

    This gives you access to all of the managed properties that have been set in the ManagedPropertyMapping element.  See below for the syntax to request the values.

    DisplayTemplateDebuggerCtxCurrentItem

    Get the index of the current item

    var index = ctx.CurrentItemIdx;

    An item display template gets called for each result you have.  This property is useful to determine if you are on the first or last result.

    Accessing Managed Properties

    The basics of retrieving values of managed properties is accessing managed properties.  You can do this in two ways by using the managed property name or the display name in the ManagedPropertyMapping element of the display template.  You’ll find your managed properties typically in a format like the one below with the display property on the left and the managed property name on the right.  The value in brackets is optional and is used by the Content Search web part and it's what the web part shows in the property mapping section as a label.

    'Line 1'{Line 1}:'Title'
    ContentSearchPropertyMappings

    Get Property Value by Managed Property Name

    ctx.CurrentItem.LastModifiedTime

    Your managed properties that you define in the ManagedPropertyMappings will show up as typed properties that you can access directly from the ctx object.  The example above display the last modified time.  Remember you can display a field from inside the HTML markup with the _#= =#_ markup.

    _#= ctx.CurrentItem.LastModifiedTime =#_

    Get Property Value by Display Name

    var line4value = $getItemValue(ctx, "Line 4");

    You can also use $getItemValue to get the value of a property using the web part display name (the value in the brackets).  This is good for the content search web part where you don't know what managed property the user has mapped to your column.  For example, in the screenshot above if I request Line 4, it will provide me the value of the Path managed property.

    Useful Properties

    There are a number of properties that might be useful to you if you plan on working with the JavaScript object model.  For example, add the following to your ManagedPropertyMapping element to get the id of the list, list item id, and site collection URL.

    'ListItemID':'ListItemID','ListID':'ListID','SPSiteUrl':'SPSiteUrl'

    Get the host list id

    var listId = ctx.CurrentItem.ListID;

    This will retrieve the GUID of the list containing the item.

    Get the list item id

    var listItemId = ctx.CurrentItem.ListItemID;

    This will return the numeric list item id of the list item.  This returns the integer value as opposed to the GUID.

    Get the site collection URL

    var siteCollectionUrl = ctx.CurrentItem.SPSiteUrl;

    The site collection URL is available with the SPSiteUrl managed property.  If you need access to the site (web) instead of the site collection, the process is a little bit more involved.

    Get the file extension

    ctx.CurrentItem.FileExtension
    

    This lets you get the file extension of the current item.  Many of the helper functions use this to determine the type of document.

    Including External Scripts and Style sheets

    Since display templates are just HTML and JavaScript, you may want to reference external script files such as jQuery.  You can also reference CSS files as well.  You need to put these references in a <script> element block of your display template.

    Include an external script

    $includeScript(this.url, "~sitecollection/_layouts/15/reputation.js");

    You can reference external scripts using a relative URL as shown above.  This example pulls the script out of the current site collection.  The first parameter to $includeScript is always this.url.

    $includeScript(this.url, https://ajax.aspnetcdn.com/ajax/jquery/jquery-2.1.0.min.js);

    You can also specify an absolute URL including those residing externally on a CDN. 

    You can also load scripts using SP.SOD as well if you prefer.  Some recommend this approach over $includeScript as it is supposed to be synchronous where $includeScript is not.

    Including a style sheet (CSS)

    $includeCSS(this.url, "~sitecollection/site/library/mycustom.css");

    Referencing a style sheet looks similar to referencing a script.

    Key Building Blocks

    When it comes to working with display templates, many of the things that you need to do must be executed after the rest of the script has rendered.  This especially applies for anything that modifies the DOM.  Refer to the four tips for using jQuery post for more details.   We use Post Render Callbacks to execute code after the display template is done rendering.  Think of these as your jQuery equivalent to $(document).ready().  There are two ways to make this happen.  The first is ctx.OnPostRender.push().

    ctx.OnPostRender = [];

    ctx.OnPostRender.push(ctx, function () {

        // do something

    });

    The first statement clears the array and the second one pushes your function onto it.  This means you can have multiple OnPostRender event handlers. Instead, you may want to use AddPostRenderCallback

    AddPostRenderCallback(ctx, function () {     // do something
    });

    If you look at the out-of-the-box templates, you'll see them executed both ways.  I tend to prefer the latter technique.

    Get a ClientContext object

    Srch.ScriptApplicationManager.get_clientRuntimeContext();

    This will return a ClientContext object for the site hosting the search component.  If you are looking to get a ClientContext object for the site hosting the search result, see this post.

    Working with the DOM

    You'll often need to keep track of some element Ids when working with display templates.  A common practice is to generate a unique base id for the containing div element and then appending strings to those ids for child elements.  This often happens at the begging of every display template and then the markup references these ids.  Let's look at some examples.

    Creating a unique element id for a child element

    var id = ctx.ClientControl.get_nextUniqueId();

    One of the first lines of script you will find in a display template is this one.  It generates a unique id that will be assigned to the first div element.  You'll then see another line such as this when is actually used by the first div element.

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

    The div element uses $htmlEncode (which we'll cover below) to html encode the id.

    <div id="_#= $htmlEncode(itemId) =#_" name="Item" data-displaytemplate="DefaultItem" class="ms-srch-item" onmouseover="_#= ctx.currentItem_ShowHoverPanelCallback =#_" onmouseout="_#= ctx.currentItem_HideHoverPanelCallback =#_">

    What do these ids look like?  Here is an example.

    ctl00_ctl53_g_f59f896a_d82a_4957_acea_90abc7a9b86e_csr1

    Getting the id of the div of the display template

    ctx.ClientControl.get_id();

    This will return the base id of the display template.

    Utility Functions

    You'll find a number of useful functions in the Srch.U object for everything from checking for null to manipulation of strings.  A lot of this overlaps with jQuery but there are some other search specific functions.  In the out-of-the-box display templates you'll sometimes see these methods referenced with Srch.U or with an alias using the $ sign.  Take a look at our first example.

    Checking for null

    if (!$isNull(ctx.ClientControl))

    {

        // do something

    }

    The above use of $isNull will execute the code in the if statement if the object is not null.  The code snippet below using Srch.U.n will also do the same thing.

    if (!Srch.U.n(ctx.ClientControl))

    {

        // do something

    }

    Checking for an empty string

    if (!$isEmptyString(ctx.CurrentItem.DisplayAuthor)) { }

    You can use $isEmptyString or Srch.U.e to check if a string is empty.

    Checking if an array is empty

    if ($isEmptyArray(myArray)) { }

    To determine if an array is empty $isEmptyArray.

    Getting an array from multi-value fields such as author

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

    The Srch.U.getArray method is handy because it knows how to split common multi-value fields such as author.  It has a list of known delimeters such as ",#", "|", and ";".

    Check if the item is a web page

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

    This method knows extensions for common page types and will return true if the extension is a page (i.e.: .aspx or .html).

    Get a friendly name for a file extension

    Srch.U.getFriendlyNameForFileExtension(ctx.CurrentItem.FileExtension);

    This method is how search displays the word PowerPoint instead of PPTX.

    Get the Display Name from an author field

    var author = Srch.U.getDisplayNameFromAuthorField(ctx.CurrentItem.AuthorOWSUSER);

    This function will display the author's full name properly.

    Setting the icon

    ctx.CurrentItem.csr_Icon = Srch.U.getIconUrlByFileExtension(ctx.CurrentItem);

    This allows you to override the icon for a given item result.  You can call Srch.U.getIconUrlByFileExtension() if you want to get the icon for the given file type.

    Get the number of items

    ctx.ClientControl.get_numberOfItems();

    You can get the number of items returned using ctx.CurrentItem.get_numberOfItems().

    Determine if the host is SharePoint Foundation

    if (!Srch.U.isSPFSKU()) { }

    Some functionality isn't available in SPF, use this method to determine your version.

    Formatting

    The Srch.U class has a number of methods that make formatting values easier.  Besides just the basic formatting, it has formatting methods for numbers, dates, and file sizes that are more human readable such as display "5 MB" instead of "5,000,000 bytes".  These method are locale aware so they should display your data in the correct format for the user.

    Formatting the date with a short pattern

    Srch.U.toFormattedDate(ctx.CurrentItem.Write, 'ShortDatePattern');

    By default, this will use LongDatePattern if you don't specify a date pattern. 

    Formatting numbers

    Srch.U.toFormattedNumber(value, 3);

    This formats the number with three decimal places if you are passing it a decimal number.

    Format the number using approximations

    Srch.U.toFriendlyNumber(value);

    This is useful for numbers in the 1,000 range and greater.  It rounds down to the nearest thousand.  For example, 52,343 will be returned as "52,000+".  If the value is greater than 100,000, it will return a value of 100,000+.

    Format file sizes

    Srch.U.toFileSizeDisplay(ctx.CurrentItem.FileSize, false);

    This will return the file size in GB, MB, or KB.  You can optionally have it show the decimal part with the second parameter.

    Formatting a string

    var myString = String.format('Hello World {0} {1}', value1, value2);

    String formatting is available just like you would use in C#.

    Truncate a URL

    Srch.U.truncateUrl(ctx.CurrentItem.Path, 40);

    Want to display a truncated path like you see in the search results.  This method is how it is done.  You can specify the number of characters in length you want it with the second parameter.

    Trim a string

    Srch.U.getTrimmedString(string, 40);

    Use this to trim a long string.  An ellipsis (...) will be added to the end of the string past the cutoff (second parameter)

    Trim Spaces

    Srch.U.trimExtraSpaces(string);

    This method is handy to trim spaces from a string.

    Localization

    Display templates have built-in support for providing localization.  It uses a system of resource dictionary stored in JavaScript files located in culture specific folders.  Each culture specific folder contains a file CustomStrings.js.  You can use this file or include your own.  

    DisplayTemplateLanguageFiles 

    Registering a resource dictionary

    If you look inside CustomStrings.js you will see the syntax of adding resources using a simple array with $registerResourceDictionary.

    $registerResourceDictionary("en-us", {

        "sampleCustomStringId": "Sample custom string",

        "rf_RefinementTitle_ManagedPropertyName": "Sample Refinement Title for ManagedPropertyName"

    });

    Loading a resource file

    Many out-of-the-box templates load a resource file automatically using the statement below.  If not, you can add it yourself or modify it if you are using a different resource dictionary.

    $includeLanguageScript(this.url, "~sitecollection/_catalogs/masterpage/Display Templates/Language Files/{Locale}/CustomStrings.js");

    Using a resource

    Using a resource in your display template is quite easy using $resource.  It's a good idea to make use of $htmlEncode to encode it before writing it.

    _#= $htmlEncode($resource("sampleCustomStringId")) =#_

    See more at #SPC14

    If you are interested in Display Templates, be sure and come see my talk (#SPC3000) at SharePoint Conference 2014.  We'll be using a lot of what you see in this post in the demos.

    Follow me on twitter: @coreyroth

  • A quick look at Office 2013 SP1 and OneDrive for Business

    If you have been following twitter at all today, you'll find plenty of details about Service Pack 1 for Office 2013 and SharePoint 2013.  I applied the update to my Surface 2, Surface RT, laptop running Office Professional Plus 2013 and Microsoft Office 365 ProPlus.  With all of my devices, everything is working so far.  You can find the update by checking Widows Update on your machine and then selecting it under the optional updates.  There are also a number of direct download links as well.

    One thing I noted on my laptop running Office 365 ProPlus (the Click-to-run install from my Office 365 subscription) is that it didn't update automatically.  I was stuck on version 15.0.4551.1512.  Thanks to @WesMacDonald for tipping me off that you can get the update to come down by Disable Updates and Re-enabling updates.  You can find the option under the Update Options button under Office Account.

    Office365ProPlusOutlookVersion

    After, you make the change just sit back and relax a bit as the change will show up before you know it.  Once it does, you may get a message like this asking you to close down Office programs.

    Office365ProPlusWeNeedToClosePrograms

    Once you do, the update will start and you will see a notification in your taskbar.

    Office365ProPlusOfficeIsUpdating

    It doesn't take long to finish.  Not more than a few minutes.

    Office365ProPlusOfficeUpdateComplete

    When it's all said and done, your version number will now show as 15.0.4569.1507.

    Office365ProPlusOutlookVersionSP1

    You will also notice that SkyDrive Pro is no more and you will have a link for OneDrive for Business.

    Office365StartScreenWithOneDriveForBusiness

    A look at OneDrive for Business

    In reality, you won't notice many changes between OneDrive for Business and the old SkyDrive Pro.  The functionality looks the same but you will just see OneDrive in all of the menus.  Here is the synchronization screen.

    Office365OneDriveForBusinessSync

    Then, here is the screen when the synchronization is in progress.

    Office365OneDriveForBusinessSyncInProgress

    The final synchronization screen.

    Office365OneDriveForBusinessSyncInProgress2

    You'll even see OneDrive for Business listed in the context menu.

    Office365DocumentsContextMenu

    Gentle Reminder

    Do all of us a favor and never refer to the SharePoint file synchronization offering as just "OneDrive".  Don't be lazy, always say "OneDrive for Business".  This will lead to people confusing it with the consumer OneDrive offering.  I don't know how many executives I had to explain the difference between SkyDrive and SkyDrie Pro to.  Help us all out and always refer to the product by it's complete name. :)

  • SPC14 Readiness Checklist

    I started doing SharePoint Conference readiness checklists back at SPC11 and I wanted to continue the tradition with #SPC14.  Mark Freeman (@SPHotShot) has continued the tradition of beating me to it and has already put out a great guide and I wanted to add my two cents.  This list may look similar to previous lists, but I have made lots of updates and additions.

    UPDATED: 2/26/2014

    What to pack:

    • Chargers / Power Supplies – I remember when I went to PDC05, I forgot my laptop charger.  I was quite bummed.  Don’t forget the chargers to your laptop, netbook, Surface, phones, etc. I have gotten a few of these new emergency phone chargers at conferences lately and they are very handy here.  Especially when you have a Nokia Lumia 920 and the battery life is terrible.  Keep in mind your average day can be 16 – 18 hours plus and you don’t want to be left in the dark and miss that big gathering because your phone died.
    • Tablet – In lieu of carrying around your heavy laptop, I find carrying a tablet at conferences to be quite handy.  I'll have my Surface 2 with me everywhere I go.  This device is perfect for conferences with it's all day battery life.  Bring whatever device or combination thereof you prefer, but keeping up on what’s going on at the conference using one of these small devices is much easier than trying to look things up on your phone.  You also can use these to fill out session evaluations.  There are usually incentives for filling out evaluations so I try to complete each evaluation right before the end of the session so I don’t forget.
    • Laptop – This used to be high on the list.  Now it's pretty low on mine.  I used to bring two monstrous W520s to conferences.  Now I am on a light-weight computer and I could actually give my session on my Surface 2 and Azure if I needed to.
    • Bring your own Internet  – The wireless networks at conferences are rarely good.  They are jammed with geeks trying to post updates on Twitter and check out what’s happening on Facebook.  If you have access to a wireless AirCard, MiFi, tethring, etc, bring it.  See if your company has any that you can check out temporarily.  SPC organizers have claimed to have rock solid Internet this year after the issues we saw at SPC12.  I'd still bring your own if you can.
    • Cash – Just a little (more if you drink and gamble a lot :) ).  There are a lot of free events but you might go do something before or after the conference and I am not a fan of running tabs at busy restaurants and bars.   Don’t take it all with you every night.  Leave some in the hotel safe.
    • Snacks and Water – After a long night, you will want something to eat.  At the minimum, you might want something to eat in the morning.  The hotels will likely have stuff in your room, but it will cost you dearly.
    • Business Cards – Even if you are not in sales, bring twice as many as you think you will need.  You will go through them faster than you think.
    • Bail Money – The Houston SharePoint Users Group has a running joke about always keeping a stash of bail money around when attending a #SharePint.  You never know what is going to happen.
    • VPN Tokens - If your work network requires a VPN token or Smart Badge you might want to bring it if you think you might need to use it.  Otherwise, you might conveniently forget it, to ensure you can focus on the conference. :)

    Before you go:

    • Arrive early – Come in early and have some fun in Vegas before you get into the conference grind.  Many of us will be arriving Friday or Saturday.  I tend to arrive on Saturdays while most of the foreign nationals I know, tend to arrive Friday or earlier.
    • Don’t leave early – After a week of Vegas, I am sick of the place and I am ready to leave.  However, you don’t want to cut the conference short on Thursday by having to leave early.  Plan for an early Friday departure.
    • Set your schedule on My SPC  - This will make your SPC organizers happy when it comes to capacity planning.  You aren’t required to go to that session you schedule, but it will help you pick from the 10+ sessions going on at any given time slot.  Go to My SPC and set your schedule now.   Not sure about a session, watch the teaser video on YouTube.
    • Create your Bio on My SPC – Whether you are an end user or a SharePoint rock star, take a few minutes to write about yourself.  Include where you work if you want along with what you typically do with SharePoint and what you want to get out of the conference.  Upload a picture of yourself to make things more personal.  Be sure and set the privacy settings as desired.  Set your My SPC bio now.  Be sure and edit your profile on Yammer too.
    • Connect on My SPC - If you go to the Communities tab, you can search for other attendees.  You can also connect your social networks on the Favorites page to find even more people.  I managed to get LinkedIn to work, but have had no success with twitter.
    • Get on Yammer - The conversations have already begun about SPC on Yammer.  This is a great way to find out what other people are doing, network with others, and talk with people with similar issues.  Most importantly, you can have a conversation about the sessions while they are occurring.  Speakers will be watching Yammer before, during, and after their sessions.  As an example, here's a link to my session #SPC3000 in Yammer.   Be sure and follow others in Yammer to have more show up in your social feed.
    • Create a #SPC14 Search in Twitter – Twitter hasn't gone away by any means, so keep an eye on the activity of the #SPC14 hash tag.  However, this year, I think Yammer is where you are going to find the most info and conversations.  Twitter will still be good to find out about sessions, events, and it will generally give you an idea of what is happening at the conference.  Be sure and include the #SPC14 hash tag on anything you post and help get this conference trending!
    • Follow @SPConf on Twitter – @SPConf is the official twitter account for SPC.  This account often posts useful stuff about the conference.  I’ve also used it to ask questions or provide general feedback and I’ve had very good luck getting a response.  SPConf has a bit quieter this year due to the use of Yammer.  Keep in mind this twitter account is effectively manned by one person, so if you have an inquiry, take it to Yammer.
    • Reach out to your local SharePoint User Group – Find out what your local SharePoint User Group is doing while at SPC.  Many of them are having meetings or socials. 
    • Don’t forget to set your user group in your profile  - You can now set your SharePoint user group in your My SPC bio.  Set that to make it easier to find people in your group.
    • Register for Pre-conference Sessions – If you think you will be able to get up on Sunday morning, attend one of the pre-conference sessions.  Some of them are free, some are not.
    • RSVP for Parties – Budgets have been cut, but parties are still out there.  Many of the "big" parties have reverted to going back to customer appreciation parties.  Getting into them is going to be difficult unless you are spending money with the vendor hosting it.   Be sure and follow @SPCPartyPatrol to find out where the parties are too!
    • Arrange for Ground Transportation  - Don’t forget to arrange for ground transportation.  You really don’t need a car in Vegas, but you do need a way to get there.  Taking a Taxi usually isn’t too expensive and there are plenty of shuttle options as well.  This may be less of a concern on arrival but more for your departure.
    • Leave space in your bag – Between the conference materials and the vendors you are going to end up with a heap of product information, trinkets, and T-shirts.  Make sure you have room in your bag to bring them home.  Otherwise you’ll be hand carrying them on the plane or leaving things behind.
    • Update your devices - Now is a great time to make sure your devices are up to date with the latest security patches.  You might even consider applying Service Pack 1 for Office 2013.  Make sure they are charged too!
    • Set your out-of-office - You're at this conference for a variety of reasons, you need to focus.  Try to stay out of Outlook and let people know that you will be slow to respond.  If you need to stay connected, I recommend picking one time of day, such as in the morning before sessions, to catch-up on what's happening back at home.

    What to do at the conference:

    • What happens in Vegas, will not stay in Vegas – Nerds have gadgets and they like to take pictures.  Do something stupid and you can rest assure it will be on twitter within seconds. :)
    • Find the official SharePint bar, the Bourbon Room – Every year there is always one bar where people tend to hang out.  At Mandalay Bay it was EyeCandy.  This year it sounds like it will be the Bourbon Room.  Make a habit of cruising by it from time to time on your way to wherever to see if anyone you know might be there.
    • Ask questions  - Don’t be afraid to walk up to the mic and ask a question.  That’s what you’re here for.  If you don’t want to ask it in front of everybody, wait in line and talk to the speaker at the podium just be mindful that the speaker has to clear out in a hurry.  Don’t be afraid to approach speakers outside the room either.  Most of them are friendly and are easily engaged using beer and cocktails. :)  It's not uncommon to find them at the official SharePint bar.
    • Make friends – You may run into lots of people you know, but many people aren’t active on twitter and aren’t familiar with the SharePoint community at all.  Find a friend if you didn’t come to the conference with any one.  It’s much more fun to go do all of the activities in a group rather than by yourself.  If you have coworkers there, feel free to hang out with them, but don't feel that you are obligated to.
    • Go to the evening events - I can't stress this enough.  Try to avoid team dinners that overlap with the events.  Get them rescheduled.  The evening events are where the real connections are made, friends are found, contracts are signed, and new jobs are discovered.  If you just go to the conference and nothing else, you are missing out on half the experience.
    • Remember to eat - This one sounds obvious but it’s not.  You may be going to lots of parties with nothing but light appetizers.  This does not give you a good base to work upon before embarking on a night of massive consumption. Pace yourself!
    • Don’t worry about writing everything down – Remember the slides and content will be on Yammer before the session.  Don’t stress out because you weren’t able to write down a URL or code snippet on a slide.   You can also take pictures of slides as well.
    • Visit the Exhibit Hall – The exhibit hall is a lot of fun.  Besides all of the SWAG and drawings, you are likely to find out about evening events that way. Make a point of going there every day.  I spend a good majority of my time there during the conference.  You'll never know who you run into.
    • Be on the lookout for the SharePoint Monkey - You'll never know what that silly monkey is going to be up to during the conference.  Be on the look out for him throughout the convention center.  Follow him on twitter @TheSPMonkey for clues!
    • Attend the sessions – Don’t skip out on the morning sessions.  If I have to get up early so do you. :)
    • Attend the Hands on Labs – If you haven’t had a chance to get your hands on SharePoint 2013, get down to the HOL and check it out.  This is a great way to experience the product without having to take the time to install it.   It opens at 9:00 am on most days (after the keynote on Monday).
    • Take a test – Certification tests are available for half price during the conference.  Go roll the dice and find some time to take a test.
    • Don’t underestimate travel times – At SPC12, the walk to the convention center from a room at the Mandalay Bay is at least ten minutes.  When I stayed at the Luxor at SPC09, it was a full thirty minute walk.  I assume the walk is just as far.  Even within the convention center, there are long walks between sessions.  Plan accordingly.
    • Set your alarm before you go out for the night - Before you go out for the night, set your morning alarm on your phone.  You may not remember when you get back to your room.
    • Arrive early to sessions – Many sessions will fill up and entrance will be denied, especially futures sessions.  Don’t get left out by showing up late.
    • Don't be afraid to leave a session - If you decide in the middle of the session, that this one isn't for you, don't be afraid to quietly step out and go see something else.  A few speakers might call you out for it, but most won't. :)
    • Learn hash tags for the sessions you are attending – Every session you are attending has an associated hash tag that you can follow.  For example, my Display Templates session number is 3000, so the hash tag for it is #SPC3000.  You can go ahead and save a search for that one now. :) 
    • Don’t wear your badge outside of the convention center – Nothing says you don’t have any game like walking out of the convention center with your badge on.  Take it off as you exit the area.  Don't lose it though as it may cost you a lot to replace it.
    • Don’t forget your badge (and lanyard) at the attendee party – At SPC09, your badge and the lanyard were required to get in.  I know several people that had to walk all the way back to their room just to get the lanyard.  That was a one hour walk since it was back to the Luxor.  They may do a wristband for the attendee party this year, so if they do, don't forget that.
    • Keep your phone charged – The battery life on LTE phones is horrible and even worse when you are tweeting non-stop all day.  Keep an eye on your phone’s battery life and charge up throughout the day. 
    • Don’t blow all your money – This one goes without saying.  I came to SPC09 on a budget and quickly depleted my designated gambling funds.  It prevented me from doing anything else for the rest of the trip.  If you gamble, keep your gambling money separate and leave some of it in the room early in the week.  This will keep you from losing it all early and you will still have some left should you get mugged. :)
    • Don’t be afraid to leave for lunch – I’m not a huge fan of conference food and it rarely gets along with my diet.  Usually by the second or third day I am grabbing anyone I can find and going off-site.  Find me at the conference and you can join me.
    • Attend #ShareHofbrau on Thursday – After the conference, unwind with friends at Hofbrauhaus Las Vegas.  It’s an authentic German beer hall and it’s loads of fun.
    • Take the pledge! - For those non-developers out there, tell the world you are tired of semi-colons!  This little sub-group organized by my wife, @JenniferMason, will have stickers for your badge (link).
    • Fill out your evaluations – These really are important to the speakers.  Let them know they did a good job and take the time to leave actual text comments in them.
    • Establish rendez-vous points – Establish meeting spots in advance with your group and set a time to meet.  Mine will probably be the slot machines immediately outside of the Bourbon Room. :)
    • Go to the attendee party – Go to the attendee party.  If you are expecting to meet people there, meet them before you leave the hotel.  As an added bonus they are serving drinks before you get onto the bus.  If you don’t walk in with the people you want to see, you will likely not see them that night.  I wouldn't have high expectations of doing a ride-along although supposedly they have quite the system to make this happen.  10k people at an event and only a handful of cars.  You do the math.
    • Hydrate - Drink lots of water throughout the day.  This is especially important if you have had a lot of late nights.
    • Don't forget to go outside - At one point during SPC09, I realized I hadn't been outside in three days.  Sneak out at some point and go find some sun.  The weather should be nice in Vegas.
    • Walk the casino floor – Even if you don’t gamble, make a habit of walking the casino floor.  You never know who you might run into.  An accidental run-in might shape the way the rest of the night goes.
    • Silence your phone during sessions, turn it on as loud as possible everywhere else - Don't be "that guy" who has your phone ringing in the middle of a session.  That's guaranteed embarrassment. When you're not in sessions, turn the volume up on it as you probably won't hear it go off otherwise when you get a message.

    That’s my list.  I’m sure there are other things to remember.  Do you have anything else to add?  Leave a comment.  This probably goes without saying, but if you are not on Yammer and Twitter, now is the time to join.  It’s the best way to keep up with what’s happening at the conference.

    I’m also presenting a sessions at this year’s conference and I would love for you to come see it.

    • #SPC3000Changing the look of Search using Display Templates (Yammer) (Schedule Builder) (Thursday, March 6th, 10:30a) – In this session, we’re going to go into the anatomy of a display template.  We won’t spend time making complex visualizations, that you’ll never use.  Instead, we’ll go into the basics of changing every aspect of a display template and how they are all connected together.  You’ll leave with code samples and supporting blog posts to walk you through every step of the journey of transforming search.

    Enough with the shameless plug. :)  Get ready and I’ll see you at the conference.

    Follow me on twitter: @coreyroth.

    Posted Feb 22 2014, 12:30 PM by CoreyRoth with 19 comment(s)
    Filed under:
  • #SPC14 Post - Using Display Templates to format search results as a grid

    Today at SharePoint Conference 2014 in session #SPC3000 on Display Templates, I showed how to format your search results as a grid when using the Content Search web part.  As promised, this blog post has gone live that walks you through the exact same steps we performed in the demo.  I've also attached the display templates in this post so that you can get started immediately.

    To create a grid with display templates, we need to create a new Control display template and a new Item display template.  The control wraps the items so this is where the start of our table will begin.  You can build your display templates with design manager, the master page gallery, or with SharePoint Designer.

    Building the Control Display Template

    I don't like to build display templates from scratch, so we'll start by taking a copy of Control_List.html and call it Control_Grid.html.  Edit the file and start by changing the title tag.  This is what is shown in the Content Search web part.

    <title>Grid</title>

    You can optionally change the description if you would like.

    <mso:MasterPageDescription msdt:dt="string">This is a grid.</mso:MasterPageDescription>

    Next give the root div a new id.

    <div id="Control_Grid">

    Ultimately, we are going to use the jQuery DataTables library to make our tables look pretty.  We'll go ahead and include all of the necessary scripts now.  I am pulling them in via CDN.  You can tweak the version numbers as desired.  Add these into the script element.

    $includeScript(this.url, "https://ajax.aspnetcdn.com/ajax/jquery/jquery-2.1.0.min.js");

    $includeScript(this.url, "https://ajax.aspnetcdn.com/ajax/jquery.dataTables/1.9.4/jquery.dataTables.min.js");

    $includeCSS(this.url, "https://ajax.aspnetcdn.com/ajax/jquery.dataTables/1.9.4/css/jquery.dataTables.css");

    The ListRenderRenderWrapper wraps each item template rendered with an li tag.  We need these so remove the two push statements.

    To work with our table and table header, we need to generate some Ids.  These Ids we'll use with jQuery code later to add elements.  We do this in the same manner as you see in other display templates used in the search center.  We use ctx.ClientControl.get_nextUniqueid() to generate a random identifier.  We then append strings onto it for our own use.  You can add the following statements anywhere in the first JavaScript block (just not inside the wrapper).

    var encodedId = $htmlEncode(ctx.ClientControl.get_nextUniqueId() + "_Table_");

    var headerRowId = $htmlEncode(encodedId + "_HeaderRow_");

    We now need to remove our ul elements and replace them with a table.  We're going to use the encodedId from above as our id.

    <table  id="_#= encodedId =#_" class="resultsTable">

    We will also want to close the table tag after ctx.RenderGroups.  Here's what the section looks like.

    var noResultsClassName = "ms-srch-result-noResults";

     

    var encodedId = $htmlEncode(ctx.ClientControl.get_nextUniqueId() + "_Table_");

    var headerRowId = $htmlEncode(encodedId + "_HeaderRow_");

     

    var ListRenderRenderWrapper = function(itemRenderResult, inCtx, tpl)

    {

        var iStr = [];

        iStr.push(itemRenderResult);

        return iStr.join('');

    }

    ctx['ItemRenderWrapper'] = ListRenderRenderWrapper;

    _#-->

        <table  id="_#= encodedId =#_" class="resultsTable">

                _#= ctx.RenderGroups(ctx) =#_

        </table>

    Building the Item Display Template

    The Item Display Template gets called once for every search result.  We need to create a display template that renders a table row as opposed to a list item.  To do this, we're going to copy Item_Diagnostic.html.  We'll call this new copy, Item_GridRow.html.  Start by changing the title tag of this file too.

    <title>Grid Row</title>

    You can optionally change the description if you would like.  Next, give the root div a new name.

    <div id="Item_GridRow">

    Now, we want to remove the existing HTML markup as we don't need it.  Remove everything between the ul tags.  Then we are going to replace it with our logic to display the table row.  If you remember, the diagnostic control, it has ten managed properties that you can map, Line 1 through Line 10.  This code simply writes out a new table row and then iterates through those lines 1 through 10.  The managed property and it's value are assigned to the lineValueInfo object.  If a managed property is mapped, it will write out the value.  If, it's mapped but there is no value, it will write out an empty table cell.  If a managed property hasn't been mapped, then it will not do anything.  This allows for the grid to feature the exact number of mapped columns and not have any empty cells.

    <tr class="gridRow">       

    <!--#_

    for(var lineNum = 1; lineNum <= 10; lineNum++)

    {

     

        var lineValueInfo = $getItemValue(ctx, String.format("Line {0}", lineNum));

        if(!$isNull(lineValueInfo) && !$isEmptyString(cbsDiagnostic_RenderPropertyMappings(lineValueInfo)))

        {

            var lineId = String.format("{0}line{1}", encodedId, lineNum);

            var slotName = String.format($resource("item_Diagnostic_SlotNameFormat"), lineNum);

    _#-->

    <!--#_

            if(!lineValueInfo.isEmpty)

            {

            if (ctx.CurrentItemIdx == 0)

                ctx.ManagedPropertyNames.push(lineValueInfo.managedPropertyName);

    _#-->

                <td>_#= lineValueInfo =#_</td>

    <!--#_

            }

            else

            {

            _#-->

                <td></td>

            <!--#_

            }

        }

    }

    _#-->       

    </tr>

    If you are getting confused with the cutting and pasting.  Don't worry because I've attached the display templates to this post.

    Trying out the display templates

    Add a content search web part to a page and then set the Control template to Grid and the Item template to Grid Row.

    ContentSearchPropertiesGridSelected

    Your results should change to look like a grid now.  It's not very pretty yet, but it's a start.

    DisplayTemplateGridNoHeader

    We can add a few fields to it using the property mapping.  Start by checking the Change the mapping... checkbox.  In my example, I am going to add Author, Write, and Path.

    ContentSearchPropertiesMapping

    Here is what our grid looks like now.

    DisplayTemplateGridNoHeaderMoreColumns

    Adding a header row

    Unfortunately, something that seems as simple as adding a header row is quite complicated.  The header row belongs in the control template.  However, we don't know what the managed properties are inside of this template.  We only have access to them from the item template.  Our technique to work around this is to collect the information in an array when the item template is executed.  Then we'll use an OnPostRender event to look at that array and build the header row.  Think of OnPostRender as your equivalent to $(Document).Ready() in jQuery.  We'll simply add this array to the ctx object.

    Go back to Control_Grid.html and add the following line after the headerRowId line.  This initializes our array.  Now we just need to add some values to it.

    ctx.ManagedPropertyNames = [];

    Now, we need to add a table header inside the table element we originally created.  Notice the use of headerRowId that we defined before.

    <thead>

        <tr id="_#= headerRowId =#_" class="resultsTableHeader">

        </tr>

    </thead>

    Now switch to Item_GridRow.html.  Here we are going to add some inside the loop.  The lineValueInfo object has the name of the managed property.  Add this snippet into the if statement checking if lineValueInfo is empty or not.

    if (ctx.CurrentItemIdx == 0)

        ctx.ManagedPropertyNames.push(lineValueInfo.managedPropertyName);

    Since the item template gets executed multiple times, we only want to add this information once.  Using ctx.CurrentItemIdx, we can check to see if this is the first iteration of this display template.

    Now we need to go back to Control_Grid.html and create the OnPostRender event.  You can add this code after the line you just added.

    ctx.OnPostRender = [];

    ctx.OnPostRender.push(function () {

        for(var i = 0; i < ctx.ManagedPropertyNames.length; i++)

        {

            $(".resultsTableHeader").append("<th>" + ctx.ManagedPropertyNames[i] + "</th>");           

        }

    });

    This code iterates through the managed properties stored in the ctx.ManagedPropertyNames array.  It then just appends a th element for each property name.

    Save your display templates and then refresh your page in the browser and now we should have the names of the managed properties above each column.

    DisplayTemplateGridHeader

    Using DataTables

    At this point, we have the makings of a good grid, but it's still quite easy.  Now is a good time to enlist the help of the jQuery DataTables plug-in.  The key to using this plug-in is having a well formatted table.  That means no missing cells or anything like that.  Luckily, I took all of the hard work out of that and our code accommodates for that.  Since we have already registered the script, it only takes one line of code to take advantage of it.  Just add this to the end of the OnPostRender function.  We just need to use the encodedId to get a reference to our table and then apply the dataTable() method to it.  I passed a few parameters to it as well such as disabling paging and sorting.  You can look up additional parameters on the plug-in's website. 

    $("#" + encodedId).dataTable({ "bPaginate": false, "bAutoWidth": true, "bSort": false });

    The entire OnPostRender method now looks like this.

    ctx.OnPostRender = [];

    ctx.OnPostRender.push(function () {

        for(var i = 0; i < ctx.ManagedPropertyNames.length; i++)

        {

            $(".resultsTableHeader").append("<th>" + ctx.ManagedPropertyNames[i] + "</th>");           

        }

     

        $("#" + encodedId).dataTable({ "bPaginate": false, "bAutoWidth": true, "bSort": false });

    });

    Now refresh your page, and you should have a nice grid that looks like this.

    DisplayTemplateGridWithDataTables

    Now that looks a lot better.  You can use your own CSS to apply your own colors of course. 

    As promised, I have attached the display templates to this post.  Feel free to try them out, use them for yourself, and improve them.  If you find them useful, feel free to leave a comment.

  • 6 tips for using SharePoint Designer with Display Templates

    SharePoint Designer is quite useful in the development cycle of display templates.  It's on the of the quickest ways to make changes.  Design Manager is probably one of the better routes, but if you have issues using it, SPD is not a bad option.  There are a few quirks though when working with SharePoint Designer, so I wanted to show my tips today.

    1) Know how to find your display templates

    The first thing to know is where to find your display templates.  This is not as obvious as you would think.  Use SharePoint Designer and open the site collection hosting your site (even if your Content Search web part is on a subsite).  You might think, I can find them under Master Pages because that's where they are actually located.  Wrong.  Here is what happens.

    SPDMasterPagesNoFilesFound

    When you browse to Master Pages -> Display Templates -> Content Web Parts, you get an empty folder.  That's because SPD is filtering for files with a .master extension.  You can tell because Master Pages is highlighted.  How do you get around this?  You have to browse using All Files.  For those of you around in the 2007 era, this might look familiar. 

    SPDAllFilesCatalogHighlighted

    Click on All Files -> _catalogs -> masterpage -> Display Templates.  Once you do, you'll be able to find all of the templates you would expect.

    SPDAllFilesDisplayTemplates

    2) Edit files in advanced mode

    Due to this little quirk, using this next tip will save you a lot of clicking.  Depending on how you open the file and then edit it, when you go back to the file list, it will reset to Master Pages and then you won't see any files in the browser.  You can avoid this, by always choosing Edit File in Advanced Mode from the context menu.

    SPDEditAdvancedMode

    3) Be aware of save conflicts

    A save conflict is going to cost you a minute of your life that you'll never get back. When it happens you will get an error like the following.

    Server error: Save Conflict

    Your changes conflict with those made concurrently by another user.  If you want your changes to be applied, click Back in your web browser, refresh the page, and resubmit your changes.

    SPDSaveConflict

    After that you will get a Save as dialog. 

    SPDSaveConflictSaveDialog

    Just click Save and surprisingly the file will save without any other issues.  I see this issue happen mostly after copying a file -> renaming it -> and then trying to save.  To work around it, copy the file, edit it first, and then rename it.

    4) Watch your encoding when renaming files

    For some reason after you copy and rename a display template, the encoding will get messed up.  Take a look at the screenshot below.

    SPDDisplayTemplateBrokenEncoding

    Notice on the ManagedPropertyMapping line it shows &#39; instead of a single quote?

    You can correct this by doing Reset to Site Definition on the file.  After you do, be sure and delete the duplicate copy it makes, because it will show up inside your Content Search web parts.

    5) If you first don't succeed, try, try again

    For some reason, when you click on All Files occasionally, it will just sit there forever and never return any results.  If this occurs, just click on something else such as Home or Subsites and then click back to All Files.  That usually fixes it.  If all else fails, close SPD and start over.

    6) Forever unghosted even after Reset to Site Definition Perhaps the biggest caveat when working with SharePoint Designer is that your files will be forever unghosted / customized.  Even if you reset to site definition, the files will remain customized as indicated by the big "i" icon.  The original file will get restored and your current version will be renamed as a copy though.  That means the good news is that you can get back to the original version.  The bad news is your file is still customized. 

    SPDAllFilesCustomized

    Keep that in mind when working with SPD.  This might be acceptable in your development lifecycle.  You can always build your templates in SPD and then deploy them later via Design Manager.

    More at SPC14

    Why do I keep blogging about Display Templates lately?  Because, I am speaking about them at my talk (#SPC3000) at SharePoint Conference 2014.  Be sure and attend if you want to learn more!

    Also if you haven't seen my post Four tips for working with jQuery and DIsplay Templates, be sure and check it out!

  • Launching a new travel blog next week

    Next week, I will be launching a new off-topic travel blog in the wake of the start of SharePoint Conference 2014.  There I'll cover tips and tricks that I have picked up in my days of travel.  Many of you I know are already seasoned experts.  However, this blog is where I'll share my own tips on travel based on my personal experience and the time I spent in the travel industry.  Will my blog feature a lot of content about Southwest Airlines?  Absolutely.  Will it cover other aspects of travel too?  Of course!  Why should you listen to me?  You shouldn't. :) Anyhow, if you have an interest in reading about travel, be on the lookout for it next week! 

    I'll see you all at #SPC14!

    Follow me on twitter: @coreyroth.

  • 4 tips for using jQuery with SharePoint Display Templates

    In preparing for my SharePoint Display Templates talk (#SPC3000) at SPC14, you'll be seeing a lot of posts from me in the coming weeks about the components that make up my demos.  Today, I wanted to talk briefly about using jQuery with display templates.  Display templates are really nothing more than JavaScript so using jQuery seems natural. However, in my experience with them, there are a few things to be aware of.

    1) Don't assume jQuery is loaded already

    The SharePoint product team has managed to do some amazing client-side things without using a single line of jQuery.  Out-of-the-box, you might find it gets loaded on occasion by certain web parts.  For the most part though, you shouldn't have any expectation of it being there.  When you start working with display templates, you might notice $ is defined, but it won't be what you think it is.  If you aren't sure it's loaded, you can always check the developer toolbar on your page.

    DisplayTemplatejQueryDeveloperToolbar

    That means you need to load it yourself.  For most of you, your first option is to include it in your master page. However, if you want to make a display template that doesn't have any external dependencies on the branding, you can opt to load the script yourself.  Display templates have a method called $includeScript to help you load external scripts.  The first parameter is always this.url.  In this example, I uploaded my jQuery file into the same folder as the rest of my display templates.

    $includeScript(this.url, "~sitecollection/_catalogs/masterpage/Display Templates/Content Web Parts/jQuery.2.0.min.js");

    You can also use this with a CDN if you prefer.

    $includeScript(this.url, "https://ajax.aspnetcdn.com/ajax/jquery/jquery-2.1.0.min.js");

    If you prefer, you can also register scripts with SP.SOD.

    2) Know how to get element IDs

    When learning about display templates, I find that one of the easiest to understand is Item_TwoLines.html.  This particular template does a good job of showing how IDs are generated and rendered.  Most templates follow this basic premise.  First generate a unique id for the display template using ctx.ClientControl.get_nextUniqueId() and append a meaningful string to it.  In this case, it generates an Id and appends _2Lines_.

    var encodedId = $htmlEncode(ctx.ClientControl.get_nextUniqueId() + "_2lines_");

    That gives us the base id in which all subsequent controls will use.  For example, the encodedId might look something like.

    ctl00_ctl53_g_4c262258_fbac_4368_83cf_626d56131ca1_csr1_2lines

    To generate the element ID for the div containing everything in the display template, it appends to encodedId with another string.

    var containerId = encodedId + "container";

    This gives us an id for the div like the following.

    ctl00_ctl53_g_4c262258_fbac_4368_83cf_626d56131ca1_csr1_2lines_container

    The display template then uses that generated id when it is rendering.

    <div class="cbs-Item" id="_#= containerId =#_" data-displaytemplate="Item2Lines">

    Now, you might be thinking, ok great, I can work with that Id and do something.  Maybe something like this even.

    $('#' + containerId).append("<p>Really good search result!</p>");

    You can do that but only at certain times.  Read on...

    3) Don't try to access the DOM in the middle of your display template

    What do I mean by that?  Let's look at the rest of Item_TwoLines.html.

    <div class="cbs-Item" id="_#= containerId =#_" data-displaytemplate="Item2Lines">

        <a class="cbs-ItemLink" title="_#= $htmlEncode(line1.defaultValueRenderer(line1)) =#_" id="_#= pictureLinkId =#_">

            <img class="cbs-Thumbnail" src="_#= $urlHtmlEncode(iconURL) =#_" alt="_#= $htmlEncode(line1.defaultValueRenderer(line1)) =#_" id="_#= pictureId =#_" />

        </a>

        <div class="cbs-Detail" id="_#= dataContainerId =#_">

            <a class="cbs-Line1Link ms-noWrap ms-displayBlock" href="_#= linkURL =#_" title="_#= $htmlEncode(line1.defaultValueRenderer(line1)) =#_" id="_#= line1LinkId =#_">_#= line1 =#_</a>

            <!--#_

            if(!line2.isEmpty)

            {

                // don't use jQuery here!

            _#-->

            <div class="cbs-Line2 ms-noWrap" title="_#= $htmlEncode(line2.defaultValueRenderer(line2)) =#_" id="_#= line2Id =#_">_#= line2 =#_</div>

            <!--#_

            }

            _#-->

        </div>

        <!--#_

            // don't use jQuery here either!

        _#-->

    </div>

    Display Templates let you do some old school classic ASP style spaghetti code.  So you might think what if I try to reference an element inside that if statement or maybe you just add a new script block past the end of the last div.  You can try it and your script will execute, but your jQuery won't find anything when you try to reference an element by id.  That's because it's not on the page yet.  If you look at the script that is generated for the display template, you will see that all of your HTML is pushed onto the ms_outHtml object.  This allows all of your display template HTML to be pushed onto the DOM at once in a more efficient manner.  How do we work around that?  Read on...

    4) Use jQuery inside of OnPostRender

    Any jQuery manipulation you want to do really needs to occur in a post render event using AddPostRenderCallback.  You just register a function and this will get executed when all of the HTML for the display template has hit the page.  Think of this is as your $(document).ready() of display templates.  Take a look at the code snippet below to see how we would have executed our previous example.

    AddPostRenderCallback(ctx, function () {

        $('#' + containerId).append("<p>Something</p>");

    });

    This will execute the line of code inside the function once the display template has rendered and then you will get the expected results.

     

    If you are just starting with display templates, hopefully you have found these quick tips useful.  We'll be covering this in more detail at my session at SPC14.  I hope to see you there.

    Follow me on twitter: @coreyroth.

  • How to: Get a ClientContext for a site given a full URL

    Last week, I was faced with the challenge of determining the URL to the site (web) when I knew the URL to a document in SharePoint.  I also happened to know the URL to the site collection, but I wanted to reliably get the URL to a web so I could get a ClientContext object for it.  Why would I want to do this?  Come to my session (#SPC3000) on Display Templates at #SPC14 to find out!  I also had someone leave a comment on my blog in just the last week about this topic, so I thought now would be a good time to share it.  @JThake suggested that he would have just parsed the path, but given a complex URL, that's not going to work!

    With the server-side object model, you can pass a full URL to the SPSite constructor and get a reference to an SPWeb object.  This trick doesn't work though with JSOM.  If you want a reference to a given subsite, you need to pass the exact URL.  This technique wasn't obvious to me or a few others, but luckily @Path2SharePoint shed some light on this when he mentioned I needed to use /_api/contextinfo.  For those of you who have doing a lot with client side code you are probably familiar with this end point because you can get your form digest that way.  Honestly though, there is not a lot of information out there on this particular endpoint.

    I did a search on the Internet to see if I could find a complete working example and things were pretty scarce.  Luckily though, I found a snippet on the bottom of a post from @Wictor that had just enough of what I needed.  Let's put it all together.  First let's assume we have a URL like the one below.  This refers to a document in buried in a folder of a document library of a subsite. 

    var documentPath = "http://server/sitecollection/site/documents/folder/mydocument.docx";

    We need to make a $.ajax() cal to the /_api/contextinfo end point, but first we need to assemble a URL that works.  To do this, we need to strip off the filename.  We'll then append our end point to the path.  We'll just use the substring function and lastIndexOf to split the path off.  If you have a library loaded to work with URLs, you can use one of those methods as well.

    documentPath = documentPath.substring(0, documentPath.lastIndexOf('/')) + "/_api/contextinfo";

    Now, we need to call $.ajax() to get the objects we need.  One thing to know is that this endpoint requires you to use POST instead of GET.  That means, you can't just try the URL in a web browser.  Here is what the code looks like.

    $.ajax({

        url: docuemntPath,

        type: "POST",

        contentType: "application/x-www-url-encoded",

        dataType: "json",

        headers: {

            "Accept": "application/json; odata=verbose",

        },

        success: function (data) {

            if (data.d) {

                var webUrl = data.d.GetContextWebInformation.WebFullUrl;

     

                var clientContext = new SP.ClientContext(webUrl);

            }

        },

        error: function (err) {

           alert(JSON.stringify(err));

        }

    );

    In the success function, we can get the information we need from data.d.GetContextWebInformation.WebFullUrl.  This has the full path to the site hosting the document.  From there, we can just pass it to the constructor of SP.ClientContext to get a reference to that subsite.  Now you can work directly with the objects on that site such as it's lists or whatever you need.

    We'll be using this snippet of code in my talk #SPC3000 at SharePoint Conference.  If you have an interest in display templates, be sure and attend!  Thanks again to everyone that helped me with this!

  • Announcing #ShareHofbrau - Returning to Hofbrauhaus Las Vegas after #SPC14

    I did a quick poll about doing ShareHofbrau again on twitter and got an overwhelming response indicating “YES!”.  Therefore, I’m pleased to announce, that we’re doing #ShareHofbrau again at Hofbrauhaus Las Vegas on Thursday March 6th at 3:30 pm.  We did this event at the last two SPCs and it was a lot of fun, so we’re doing it again.  Last year, we had at least 30 – 40 people show up.  I think we can do better this year!

    If you’re not familiar with it, Hofbrauhaus is an authentic German beer hall just off the strip.  Basically, picture a place where Oktoberfest is everyday and they serve exclusively Hofbrau beers.  I know a lot of people are flying home on Thursday night, but if you’re staying until Friday, you should attend this event!  So come by and have a beverage before you leave Vegas and use this as the last opportunity to talk to SharePointers from all over the world.  It’s a great way to network and unwind after a great week at the conference.

    Hofbrauhaus is located not far off the strip at 4510 Paradise Road near the Hard Rock Hotel.  So come ready to talk about SharePoint and eat and drink some fine German food and beverages.

    If you are interested in coming, indicate your interest by going to the link below.

    http://sharehofbrauspc14.eventbrite.com

    As a reminder, you will be paying for yourself at this event so make friends with some of the exhibitors in the expo hall if you are looking for a free ride. :)

    For more information follow #ShareHofbrau on twitter.

    Thanks and see everyone there!

    @coreyroth

    Come see my session on Thursday at 10:30 am on Display Templates (#SPC3000).

More Posts Next page »
2012 dotnetmafia.
Powered by Community Server (Non-Commercial Edition), by Telligent Systems