Wednesday, December 31, 2014

Log Managed Property Values Retrieved From a Display Template

Hello Interwebs!
I have been doing a ton of search work lately and just wanted to share a useful script for tracking down those pesky Managed Property Values.  I have been wanting to write a search series, but just can’t seem to find the time to put pen to paper (or fingers to keyboard in my case) to get them written up!

When I first started working with search I think my biggest headache was to tell which managed properties were being retrieved and what the values were for each managed property.  The below script can be added to your display template to log the managed property name & value to your console window for each search result returned.

for (var p in ctx.CurrentItem)
 console.log(p + ":" + $getItemValue(ctx, p));

This code can be implemented within any display template, but for this example I will be using the Item_CommonItem_Body display template
(NOTE after your test you should remove the code snippet due to compatibility issues for some browser versions & that there really isn’t a reason to log these values to console outside of testing) I am using this particular template as an example, but any display template which is called from search could be used.
Below is the default code from Body tag of our Item_Common_Body display template where I have added the script to iterate through the managed properties.

    <div id="Item_CommonItem_Body">

        var id = ctx.CurrentItem.csr_id;
        var title = Srch.U.getHighlightedProperty(id, ctx.CurrentItem, "Title");
        if ($isEmptyString(title)) {title = $htmlEncode(ctx.CurrentItem.Title)}

        var useWACUrl = !$isEmptyString(ctx.CurrentItem.ServerRedirectedURL);
        if(ctx.ScriptApplicationManager && ctx.ScriptApplicationManager.states){
            useWACUrl = (useWACUrl && !ctx.ScriptApplicationManager.states.openDocumentsInClient);

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

        var showHoverPanelCallback = ctx.currentItem_ShowHoverPanelCallback;
        if (Srch.U.n(showHoverPanelCallback)) {
            var itemId = id + Srch.U.Ids.item;
            var hoverId = id + Srch.U.Ids.hover;
            var hoverUrl = "~sitecollection/_catalogs/masterpage/Display Templates/Search/Item_Default_HoverPanel.js";
            showHoverPanelCallback = Srch.U.getShowHoverPanelCallback(itemId, hoverId, hoverUrl);
        var displayPath = Srch.U.getHighlightedProperty(id, ctx.CurrentItem, "Path");
        if ($isEmptyString(displayPath)) {displayPath = $htmlEncode(ctx.CurrentItem.Path)} 
        var url = ctx.CurrentItem.csr_Path;
                url = ctx.CurrentItem.ServerRedirectedURL;
            } else {
                url = ctx.CurrentItem.Path;
        ctx.CurrentItem.csr_Path = url;
        var pathLength = ctx.CurrentItem.csr_PathLength;
        if(!pathLength) {pathLength = Srch.U.pathTruncationLength}

        var maxTitleLengthInChars = Srch.U.titleTruncationLength;
        var termsToUse = 2;
        if(ctx.CurrentItem.csr_PreviewImage != null)
            maxTitleLengthInChars = Srch.U.titleTruncationLengthWithPreview;
            termsToUse = 1;

        var clickType = ctx.CurrentItem.csr_ClickType;
        if(!clickType) {clickType = "Result"}        
        <div id="_#= $htmlEncode(id + Srch.U.Ids.body) =#_" class="ms-srch-item-body" onclick="_#= showHoverPanelCallback =#_">
            if (!$isEmptyString(ctx.CurrentItem.csr_Icon)) {
                <div class="ms-srch-item-icon"> 
                    <img id="_#= $htmlEncode(id + Srch.U.Ids.icon) =#_" onload="'inline'" src="_#= $urlHtmlEncode(ctx.CurrentItem.csr_Icon) =#_" />

                                          var titleHtml = String.format('<a clicktype="{0}" id="{1}" href="{2}" class="ms-srch-item-link" title="{3}" onfocus="{4}" {5}>{6}</a>',
                                          $htmlEncode(clickType), $htmlEncode(id + Srch.U.Ids.titleLink), $urlHtmlEncode(url), $htmlEncode(ctx.CurrentItem.Title), 
                                          showHoverPanelCallback, appAttribs, Srch.U.trimTitle(title, maxTitleLengthInChars, termsToUse));
            <div id="_#= $htmlEncode(id + Srch.U.Ids.title) =#_" class="ms-srch-item-title"> 
                <h3 class="ms-srch-ellipsis">
                    _#= titleHtml =#_
            if (!$isEmptyString(ctx.CurrentItem.HitHighlightedSummary)) { 
                <div id="_#= $htmlEncode(id + Srch.U.Ids.summary) =#_" class="ms-srch-item-summary">_#= Srch.U.processHHXML(ctx.CurrentItem.HitHighlightedSummary) =#_</div>

            var truncatedUrl = Srch.U.truncateHighlightedUrl(displayPath, pathLength);
            <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) =#_')" >
                _#= truncatedUrl =#_

        if (!$isEmptyString(ctx.CurrentItem.csr_PreviewImage)) 
            var altText = Srch.Res.item_Alt_Preview;
                altText = ctx.CurrentItem.csr_PreviewImageAltText;

            var onloadJS = "var container = $get('" + $scriptEncode(id + Srch.U.Ids.preview) + "'); if(container){ = 'inline-block';}" +
                           "var path = $get('" + $scriptEncode(id + Srch.U.Ids.path) + "'); if (path) { Srch.U.ensureCSSClassNameExist(path, 'ms-srch-item-preview-path');}" +
                           "var body = $get('" + $scriptEncode(id + Srch.U.Ids.body) + "'); if (body) { Srch.U.ensureCSSClassNameExist(body, 'ms-srch-item-summaryPreview');}";

            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>', 
                                            $htmlEncode(clickType), $urlHtmlEncode(url), appAttribs, $urlHtmlEncode(ctx.CurrentItem.csr_PreviewImage), $htmlEncode(altText), onloadJS);
            <div id="_#= $htmlEncode(id + Srch.U.Ids.preview) =#_"  class="ms-srch-item-previewContainer"> 
                _#= previewHtml =#_
         for (var p in ctx.CurrentItem)
       console.log(p + ":" + $getItemValue(ctx, p));
Now that we have added the script, saved and published the display template we are ready to see what is brought back from our search results!

  1.  Open your browser’s developer tools
  2.  i.e. FireBug, Chrome Tools, F12, etc.
  3.  Enter a search query into the search box which will return results
  4. You can use * to grab everything
  5. Select the console tab in your developer tools
  6. Bask in the awesome... There are actually more properties for this particular item than the screenshot shows, but I can only grab so many with a screen capture :) 

In a future post I will be walking through how to add new Managed Properties to your display templates and how to surface them via the item results and hover panels.

Hope This Helps!

Tuesday, July 22, 2014

Setting Up Your First Device Channel in SharePoint 2013


Today I will be walking through how to set up your first device channel, as well as explaining what a Device Channel is.

So What Is a Device Channel?
Device channels are new to SharePoint 2013 and are only available when the publishing features are turned on, so if your site is a 2013 publishing site read on!  Device channels enable you to provide a different user experience based upon the device or even browser the user is accessing your site from.  Device Channels enables the site owner to route users to a specific master page and CSS file depending upon the device or browser which is being used to access the content.  An example scenario where this might be useful is to create a tablet friendly version of your publishing site for field users.

So What Will We Be Doing Today?
Today we will be setting up a very simple device channel for an iPad Mini where we customize the page to display the text “iPad Mini”, when accessed by an iPad Mini.

What We Need:
  • A SharePoint 2013 Publishing Site
  • The device we want to create the channel for
  • SharePoint Designer 2013
  • This website to find our User Agent String
User Agent String:
The user Agent String is needed in order to set up the device channel and we will be using it in a later step when we fill out our Device Inclusion Rules.

Navigate to on the device that you would like to create a channel for.  This site will provide the User Agent String, this is the one I used for the iPad Mini
Mozilla/5.0 (iPad; CPU OS 7_1 like Mac OS X) AppleWebKit/537.51.2 (KHTML, like Gecko) Version/7.0 Mobile/11D167 Safari/9537.53
Save the user string to Notepad++ or in any text editor so you can refer to it later

SharePoint Designer:
We will use SPD to copy the .HTML of the file that we would like to begin working from, for this example I will be using the default Seattle.html.  When we copy the .HTML and paste it into the Master Page gallery the corresponding Master Page will automatically be created (don’t copy and paste the Master).

  1. Open SharePoint Designer and then open the site you are creating the device channel for
  2. Select Master Pages under Navigation
  3. Right click and copy the .html file that you would like to use as a starting point
  4. Right click and paste the .html file that you copied in 3.1.  It will create an identical copy of the file except it will append a _copy(1) onto the end of your file name, which we will rename in our next step.
  5. Select the hyperlink of the .html page you just created in order to see its properties.  From this page we will rename our file.  For the purposes of this blog post I am going to rename mine to “blogPost.html” but you could name your page something which corresponds to your device channel i.e. iPadMini.html
  6. Right click the tab name at the top of the page and select Save
    1. Select Yes to the warning about renaming the page.  This dialog will rename the corresponding Master Page and update the links.
  7. Select Master Pages
    1. You should now have the .HTML file you just created as well as a corresponding .Master

Now that we have the html and master page file in place, we can add a simple change to the .html page in order to make sure our device channel is indeed working once we complete the set up.  For the purposes of the tutorial I will just be adding a <h1></h1> tag with the verbiage “iPad Mini”, but in a real world scenario we would most likely add styling and functionality.

HTML Page:
  1. Right click your page that you just created and select Edit File In Advanced Mode
  2. Right under your opening <body> tag add the <h1>”whatever text here”</h1>.  For my example I will be using <h1>iPad Mini</h1>
  3. Right click your page’s tab and save your changes
  4. Exit SPD
SharePoint Site:
The next few steps will involve navigating back to our SP site, checking in the pages if they are still checked out, publishing them as major versions, and then finalizing the device channel.
  1. Navigate to the URL of your SP site
  2. Select the Gear Icon
    1. Select Site Settings
  3. Under Web Designer Galleries
    1. Select Master Pages and page layouts
  4. Find your html and master page that we created in the previous steps
  5. Right click your .html page or .master and select publish a major version

Finalizing the Device Channel:
The next few steps will actually implement the Device Channel and will enable you to see what your site will look like for users accessing the site from the device you have specified.
  1. Select the Gear Icon
    1. Select Site Settings
  2. Select Device Channels under Look and Feel
  3. Add a new item to Device Channels
    1. Fill out all of the fields in the form, I would explain them but there is a description under each field in SP so I will be skipping this step.  The trickiest bit is the Device Inclusion Rules which is really just looking for your user agent string which we did in the first step.
    2. Paste your User Agent String into the Device Inclusion Rules
    3. Select Activate

Activating the Device Channel:
  1. Select the Gear Icon
    1. Select Site Settings
  2. Select Master Page under Look & Feel
    1. You should now see an option for the Site Master Page you just set up
    2. Set the dropdown to the master page you would like to use for this device
    1. Select OK 
Testing the Channel:
There are two ways to check to see if the device channel was set up correctly, you could either use a test device that you selected (always a good excuse to get a new piece of hardware!) or the less fun way of adding ?&devicechannel=”alias name”.  A working example would be for my site would be “start of URL/sites/danspubtest?&devicechannel=iPadMini

Check It Out!
Congrats!!!!!  If you have followed this tutorial, you should have just viewed your site with the heading that we added earlier specific to that device!J


Tuesday, July 8, 2014

Form / Data Validation Using jQuery & Javascript

Hello Readers,

It has been a while since my last post and I apologize for the delay!  Today I wanted to talk about how to use jQuery and JavaScript to validate field values in SP forms.  This example will be extremely simple, but hopefully it will help cover the basics, as well as spark some ideas on how you might want to validate entries on your forms.  You will need to add this script to the new & display form.aspx pages with either with a content editor or script editor web part.

If you are just getting started with JavaScript and jQuery I would highly recommend reading through Mark  Rackley’s  A Dummies Guide to SharePoint and jQuery-Getting & Setting SharePoint Form Fields which is a fantastic blog series as well as Marc Anderson’s  Blog which is loaded with great content.  Both of their sites run through all of the selectors i.e. when to use select, input, etc. so I will not be covering that in this post.

For my example today I have a simple custom list with the following fields:
            Title: (single line of text) (input field)
            Candidate Hired: (combo box dropdown N/A, No, Yes) (select field)
            Candidate Start Date: (date & time control)

I want to an alert to pop up letting the user know that they accidentally left the Start Date blank when Candidate Hired = “Yes” & the Candidate Start Date was left blank.  If we were going to rely on this script for validation we would want to put in other conditions to handle scenarios such as having a start date without selecting yes, but for the sake of simplicity we will keep it very basic.

To Do This We Will Need:
·         A reference to the jQuery library
o   We can reference the library either with a CDN (content delivery network) or by downloading the library and storing it somewhere on our SharePoint site and linking to it there.
o   If you download the library I would recommend saving the file with a title of jQuery and adding an additional field to the library with the version number.  This is a good practice since if you have multiple scripts linked to the jQuery library, they won’t break when a new version is uploaded and the file name changes.
·         This Script
<script type="text/javascript" src="LINK TO jQuery Location"></script>
 <script type="text/javascript" language="javascript">
 function PreSaveAction() {
drop = $("select[title='Candidate Hired']").val();
date = $("input[title='Candidate Start Date']").val();
    if (drop ==="Yes" && date===""){
        alert("You Shall Not Pass, Fix The Date!");
        return false;
        return true;

How Does The Script Work?
Essentially what this script does is to check to make sure that the Candidate Start Date is not left blank if the user indicated they were hired before they can save the form.  We are obtaining the values based on the form’s display names **Not Internal Names!!** and then checking those values before the form can be saved.  If the form matches our criteria of Hired = yes & Start Date = “”, then our script will pull a Galdalf when the user tries to submit the item with a “YOU SHALL NOT PASS” until the form has been fixed.  

In order to repurpose the script you could swap out the display names and use it as is or you could use it as a template to start writing your own validation scripts.  Although some validation can be done via the column validation and list validation in SharePoint, I always run into scenarios where a simple script enhances the user’s experience as well as ensures the inputted data is correct.  An example of this would be to use JavaScript to create an input mask for form fields or to ensure that an email address field actually contains @ & .com.

Well again this script is super basic, but I hope that this sparks some thoughts on where your forms could be enhanced and how you can use jQuery and JavaScript to get it done!

Best Regards,

Monday, May 19, 2014

How to Hide Site Actions (or any control) Based on Permissions:

Hello Blog Readers!

It has been a while since I have had something interesting to write about, but I wanted to go ahead and post an article on how to hide the site actions button from those pesky users based on permission levels!

**Note hiding and disabling access are two different things.  The users would still be able to access the content if they knew the URL such as… path/_layouts/settings.aspx would still let them hit the site settings even if you hid the site actions control.**

In order to hide the control based upon permissions we will need:
  1.  SharePoint Designer
  2. This Code
    1. <SharePoint:SPSecurityTrimmedControl runat="server" Permissions=”DesiredLevel1,DesiredLevel2,etc”>
      <control to hide>
      </control to hide>
  3. A control you want to hide
For this example I want to hide the Site Actions from every page in my site, so I will be editing the Master Page.

  1. Open SharePoint Designer
  2. Connect To Your Site
  3. Under Navigation
  4.  Select Master Pages
    1. Select The Desired Master Page
  5. Right Click The Master Page & Select Edit in Advanced Mode
  6. Check Out The Page
After completing steps 1 through 6, we should be ready to wrap the ms-siteactionsmenu span

 <span class="ms-siteactionsmenu" id="siteactiontd">

with our security control in order to hide it based upon permissions.  When we wrap this span in the security trim make sure to find the span's corresponding end tag (</span>).  Depending on how your master page is set up, it could be 30, 50, 100 lines down from your opening span tag.  I typical do a find and search for </span> which typically makes it painless to find.

Now the fun part, lets wrap the span in our security trim control! For the sake of space, I am not including all of my code which would be in between the span.  Instead I am using a very technical note to represent it, aka "Tons of master page code here".

The next step is easy, above your span input

<SharePoint:SPSecurityTrimmedControl runat="server" Permissions=”DesiredLevel1,DesiredLevel2,etc”>
span class="ms-siteactionsmenu" id="siteactiontd">
"Tons of master page code here"

Finally, replace the desired permission level placeholders with the permission levels which are allowed to view and use the hidden control.  The link below has the exhaustive list to choose from.

After you have input the desired permissions, save the updates, check in the page, and publish it if necessary.  I hope this article was able to help and feel free to leave a comment if there are any issues!


Thursday, March 20, 2014

How To Hide The "Get Organized With This Task List" / "Stay Tuned Updates Coming Soon" From SharePoint 2013 Timeline


I just ran across the need to hide the Stay Tuned Updates Coming Soon box from the SharePoint 2013 Time Line.  To be candid I have just really started playing around with 2013 and could not find a way out of the box to easily hide the dialog, so naturally I turned to coding like a good nerd should!  I had a business requirement that wanted the dialog hidden since it really didn't make sense to have that dialog when the list was simply displaying four tasks for the entire year.

To hide this from the page you can either use a content editor and link to the a script file containing the code below or by adding a code snippet web part to the page.  This will hide the dialog from everyone though, so just keep that in mind!


Super short post, but hopefully it was helpful for the particular scenario!


Thursday, March 13, 2014

How to Create Views on Document Set Contents

Hello Cyber People!

I can safely say if you are taking the time to read this post that you have been asked a question which had recently been asked of me “How the heck do we create views on the document set contents?”

Well the answer is a bit obscure and not as user friendly as you would imagine.  In this How To we will actually be editing the document set page, removing the document set contents, and replacing it with a list view web part of the document library.

You have enabled Document Sets Content Types
You have added a Document Set Content Type or Custom Document Set Content Type to a library
You have added one line item using the Document Set aka you have at least one document set in your library uploaded.
  1. Open a document set you currently have already uploaded in the library
  2. Select Page Select Edit Page
By editing this page, we will actually be editing the homepage for all of the document sets which are of this content type.
  1. Delete the Document Set Contents web part 
  2. Insert a new Web Part / App Part depending on what version of SharePoint you are using of the library that you are currently working in.  For my example I will be putting my Promotions Library within the document set.
My Set Up:
Library: Promotions, Document Set: Promotion, Document Set Contents will be replaced with a list view web part of Promotions.

What I essentially am asking you to do is commit Document Library Inception!  We will be putting this library / list view within the document set.  The document set should automatically send the library / list view the parameter from the query string only showing documents for that particular document set.

After you insert the library view into the document set you should be able to edit the web part and select the view that you would like to display.  For my example, I have grouped my documents by content type.


Monday, February 3, 2014

InfoPath 2010 & SharePoint 2010 Repeating Table Pulling The Wrong Information Based Upon Combo Box Order Selection


This issue was extremely hard to tag for a blog post, but essentially the Repeating Table displays correctly only when selecting items in reverse order of the dropdown i.e. selecting Choice 3, Choice 2, and Choice 1in that order pulls over the correct information.  If I were to select Choice 3, Choice 1, & Choice 2 the information would not query correctly.

The issue is actually a result of your rule which pulls in the additional information.  In my example, I have used a repeating table to select multiple products to one purchase order.  If you select the items in the incorrect order the SKU & Price will query incorrectly.  For my setup, I have a price list which drives my Material Description list box, which then pulls over the corresponding SKU & Price.

(note: this screenshot encompasses the fixed XPath rule)

To Fix This Issue:

You will need to update the XPath of the rules you created to pull over your other fields (mine being SKU & Price).

      Open your rule which sets the values of the other fields
     Open your set a fields value parameter
Select the insert function button (fx)
Select Edit XPath
Your original rule most likely will resemble the following:
a.    xdXDocument:GetDOM("Price List")/dfs:myFields/dfs:dataFields/d:SharePointListItem_RW/d:Part_x0020_Number[../d:Title = xdXDocument:get-DOM()/my:myFields/my:group1/my:group2/my:Material]

      Update the last parameter of the rule “Title = …” to current()

a.    xdXDocument:GetDOM("Price List")/dfs:myFields/dfs:dataFields/d:SharePointListItem_RW/d:Part_x0020_Number[../d:Title = current()]

This will resolve your issue and enable you to select from your dropdown in any order and still pull the correct details for your fields that you are setting.



Friday, January 17, 2014

How To Hide Save & Close For Surveys Using JavaScript

Hello Inter-webs!

Why Hide The Save & Close Button?

Before I jump into the how, lets talk about the why.  The main reason to hide the Save and Close button is due to the issues it causes for Site Admins and their users.  When a user selects Save and Close instead of selecting Finish, SharePoint treats the survey response as a working draft.  That means that the only individual who can actually access or even see the survey is the individual who created it.  Even as a site collection administrator you can only see the correct number of surveys by navigating to the /_layouts/viewlsts.aspx.  This will show you the total responses including the "drafts" but you still can't access them, so its better to hide the button all together!

How To Hide The Save and Close Button

To hide the Save and Close button you will need to:

  1. Upload the JavaScript file to your Style Library
  2. Place a Content Editor Web Part on your New & Edit forms for your survey
  3. Link the Content Editor Web Parts to the JavaScript file
  4. Hide each Content Editor Web Part
JavasScript Needed:  Save this Script into a .txt file and save it to your Assets Library

<script type=”text/javascript”>
 var x=document.getElementsByTagName(“input”);
 for (var i=0; i<x.length; i++)
 if (x.item(i).type==”button” && x.item(i).value==”Save and Close”)


Thursday, January 2, 2014

SharePoint 2010: Conditionally Format Fields in a List


This post will be covering how to use a data view web part to apply conditionally formatting to a list.  You might want to use this technique to create simple heat maps for KPIs or as a visual indicator.  An example of when this could be used is to color code expenses which are valued over $1,000.

Materials Needed:
  1. SharePoint Designer 2010
  2. A web page
  3. A List
  4. Dataview web part

In this post I will be assuming that you know how to obtain SharePoint Designer 2010, create a blank wiki or web parts page, and have a list already created that you need to conditionally format.

Now that we have all of that out of the way, let us move onto the How To!

  1. Open SharePoint Designer
  2. Open Your Site
  3.  Enter Your Credentials
  4. Open the library where the page you created is stored, this is most likely in the pages or Site Pages library
  5. Right click the page that you created and select “Edit File In Advanced Mode
After opening the page in advanced mode you should be presented with Design, Split, or Code view.  We will let SharePoint Designer do the heavy lifting for this exercise, so select the Design view.

1.    Click into the web part zone that you would like to have your list displayed
2.    Select the Insert Tab on the Ribbon
3.    Select Data View
4.    Select Your List

You should now have your list on the page as a data view web part.  I have created an example list to show how we can use conditional formatting to better visualize our data.  I will be creating a visual heat map only for my Relationship field for the sake of time, but the same concept could be applied to all of my other list fields.

  1. Select your td
  3. Select List View Tools
  4. Select Options
  5. Select Conditional Formatting
  6. Select Format Selection

  7. You will be prompted with the Condition Criteria Dialog Box
  8. Select the field you would like the condition based upon.  I will be using my Relationship field in order to create my conditions.

  9. Select the comparison and value which makes sense for your situation.  Since this list is only an example I have created extremely basic criteria.
  10. Select Set Style
  11. This dialog will provide you a list of options which could be used to conditionally format your fields.  I will be setting my Background to match the color of the entry.

You will repeat these steps for each condition that you would like to conditionally format for.  Don’t get confused by the term selection, the formation rules will be applied to all of your line items within the column.  I never had to click another td while creating my rules, the conditions will automatically cascade down.

The last step is to save your changes and to do a refresh!  Go check out your conditionally formatted data view web part!

I hope everyone was able to get this to work and fit their needs.  Please feel free to leave a comment or reach out if you have any questions J