Showing posts with label sharepoint 2013. Show all posts
Showing posts with label sharepoint 2013. Show all posts

Friday, January 22, 2016

PowerShell: Activate Nintex Workflow For a Site Collection (Includes all Sub Sites)

Recently I was on a project which was using Nintex Workflow 2013 and we needed to have the site collection feature activated, as well as the Nintex Workflow 2013 site feature on all of the sub sites within this particular site collection.  Well this was an intranet site which was comprised of 50 sub sites which would all need to be enabled.

In order to use Nintex Workflow the site collection feature must be activated as well as the site feature for any sub site which wishes to use the Workflow Product.  As much fun as manually clicking through 50+ sites and activating the feature would be, I found a script online and tweaked it a bit to be a bit more robust.  Original script author Vadim Tabakman wrote the bulk of the script, my additions are noted in the script comments.



Best Regards,
Dan


#Orignal Script Author: Vadim Tabakman
#http://www.vadimtabakman.com/nintex-workflow-enable-the-nintex-workflow-feature-on-all-subsites.aspx

#Updates By Dan Adams
    #Added a param for re-usablilty rather than have a set path
    #Added the logic to enable the Site Collection Nintex Workflow Feature before enabling the sites
    #Wrapped the Enable-SPFeature calls to handle errors if the Feature was already activated on the site

param($url = $(Read-Host -prompt "Root Site Collection Path"))

#Get the PowerShell Snapin
Add-PSSnapin "Microsoft.SharePoint.PowerShell"

#Get Web
$web = get-spweb $url
#Get Site
$rootSite = Get-SPSite $url

#Activate Site Collection Feature If Not Activated
$nintexWFCollectionFeature = "0561d315-d5db-4736-929e-26da142812c5"
(Enable-SPFeature -Identity $nintexWFCollectionFeature -ErrorAction SilentlyContinue -Url $url) -$null


#Loops Through the SubSites and Activates Nintex Workflow Web Feature
function EnableNintexWorkflowSiteFeature( $w )
{
    $ws = $w.Webs;
    foreach( $subweb in $ws)
    {
        EnableNintexWorkflowSiteFeature($subweb)
    }
    echo 'Enabling Nintex Workflow on site : ' $w.Url
    (Enable-SPFeature NintexWorkflowWeb -ErrorAction SilentlyContinue -url $w.Url) -ne $null
}

echo 'Enabling Nintex Workflow on site : ' + $web.Url
EnableNintexWorkflowSiteFeature $web

PowerShell: Loop Through All Sub Sites in a Site Collection

So this post will be extremely short, but hopefully useful.  The below script is a good starting point if you have a repetitive task that you need to complete on all of your sub sites within a particular site collection.  This script will loop through all of the sub sites, including sub sites of sub sites and print out the site's title and URL.  To use the script you simply need to run the script and input the site’s root URL without quotes.  So if my site collection was http://danssandbox.com/sites/dansSand , I would simply need to input that URL when prompted by the script for the “Root Site Collection Path.”  

This script is meant as a starting point to include actual logic rather than the Write-Hosts, but it should get you on the right track!  Enjoy the loops!


Dan


param($url = $(Read-Host -prompt "Root Site Collection Path"))

#Get the PowerShell Snapin
Add-PSSnapin "Microsoft.SharePoint.PowerShell"

#Get Root Site
$root = Get-SPSite $url

#If site was found / valid
if($root -ne $null)
{

     foreach($subSite in $root.AllWebs)
      {
       $subSiteTitle = $subSite.Title
                Write-Host $subSiteTitle -ForegroundColor Magenta
                $subSiteURL = $subSite.Url
                Write-Host $subSiteURL -ForegroundColor Cyan
       $subSite.Dispose()
      }

        $root.Dispose()

}

Thursday, November 19, 2015

SharePoint 2013 Result Source Only Include Content From Current Site NOT Sub Sites

Hello Blog Readers,

Sorry for the delay in posts recently things at work in terms of projects, but I did have an extremely helpful Result Source Syntax you should know!  From the title of this blog article you have probably guessed what I will discuss, how to actually only search on the current site!

I can’t believe how many copy and paste blogs are out there since everyone’s “solution” of Path:{Site} or Path:{Site.URL} does not work!  I was so frustrated after clicking through pages of Google links (dear god I made it to page 3… where results go to die) that I just sat down and tried to figure out a solution myself.
For those of you who don’t want the journey or explanation the key to your success is to include this as part of your query:
WebId={Site.ID}

Why is The Path Solution Incorrect?
The Path solution is incorrect since Path:{Site} will include any content from your sub sites which matches the criteria.  This is because the child sites includes the path of the parent site, which means you will always get the data from the sub sites which inherit from the parent’s Path.  You may be thinking well Dan, why don’t you just use Path={Site} instead of contains.  If you have tried this syntax on its own, you most likely only received the Home Page of the site from which you were running the query from.  Content within the site obviously does not have the URL of the homepage, so using equals will only return the one page which matches the path exactly.  In short, you can use Path:{Site} when you are trying to exclude contents from parent sites, but include content from the current site and it’s children.

How to Return Results ONLY From the Current Site:
To only obtain results from the site from which the query is being run, WebId={Site.ID} should be used.  WebId is an out of the box managed property which houses the site’s GUID  / ID, so essentially what we are saying in the above is that we want to only return items where the WebId is equal to the current site’s GUID/ID from which the query is being issued from.

Simple Example Query:
SPContentType=”Item” AND WebId={Site.ID}

This query will only retrieve items which match the item content type display name and where the WebId is equal to the current sites GUID / ID.  If you were to try this same query from a top level site utilizing Path:{Site} instead of WebId, like the other blogs and sites suggest, you will retrieve Items with this content type from all of the sub sites beneath this path.  The query above will ONLY return items from the current site J
Best of Luck!

Dan

Monday, July 27, 2015

Apply a Retention Policy to a Set of Content Types Via PowerShell

Hello Readers!

Recently it seems that I have been doing more and more PowerShell, this time I needed to apply a retention policy to a set of Content Types.  In order to do this I wrote a PowerShell script which checks all of the content types within your site against a list of content types stored in an array.  If the content type name matches the name within the array we apply the policy.  My policy is extremely simple and simply marks the item as a record when today is equal to my field ExpirationDate.

Many users will need a policy much more robust than this and my advice is to download SharePoint 2013 manager in order to create them https://spm.codeplex.com/.  With SharePoint Manager 2013 you can open an existing policy that you have made via the GUI and extract the XML.  The script needs the XML markup in order to create the policy and writing it by hand is horrible!

So you may be wondering, what do I need to change?  The only parts of the script which would require modification would be the content type names within $parentCtypes and the XML for the retention policy.  After you have updated the XML for your policy and selected which Content Types to apply the policy to, you simply need to run the script.  The script will prompt you for the root site collection URL.

I actually struggled the most with trying to decipher the correct markup of the XML, the script itself was actually pretty simple.  Again, in order to get the markup for the policy I would create it via the GUI and then find the policy with SharePoint 2013 Manager and view the XML for the policy, it is way easier with a copy paste!  I wasted a ton of time trying to write the XML from scratch and advise you not to do the same!

param($url = $(Read-Host -prompt "Root Site Collection Path"))

#Get the PowerShell Snapin
Add-PSSnapin "Microsoft.SharePoint.PowerShell"

#Search Service Application
$ssa = Get-SPEnterpriseSearchServiceApplication
Write-Host $ssa.Name -ForegroundColor Magenta

#Get Site
$site = Get-SPSite $url

#Get Root Web
$web = $site.RootWeb

#Write Out That The Web Was Found
if($web -ne $null)
{
    Write-Host "The web is" $web "and the site is" $site.HostName -ForegroundColor Magenta

}

#Fill the array with base content types i.e. Exelon Content Page
$parentCtypes = @("Content Page");

foreach ($ctype in $web.ContentTypes)
{
    foreach($i in $parentCtypes)
    {
        if($ctype.Name -eq $i)
        {
            Write-Host $ctype.Name "is the same as" $i -ForegroundColor Magenta
            $ctypePolicy = [Microsoft.Office.RecordsManagement.InformationPolicy.Policy]::GetPolicy($ctype);
            if($ctypePolicy -ne $null)
            {
                Write-Host $ctype.Name "Has an existing Policy" $ctypePolicy "and is being deleted" -ForegroundColor Magenta
                [Microsoft.Office.RecordsManagement.InformationPolicy.Policy]::DeletePolicy($ctype);
            }
            [Microsoft.Office.RecordsManagement.InformationPolicy.Policy]::CreatePolicy($ctype, $null);
            $ctypePolicy = [Microsoft.Office.RecordsManagement.InformationPolicy.Policy]::GetPolicy($ctype);
            $ctypePolicy.Items.Add("Microsoft.Office.RecordsManagement.PolicyFeatures.Expiration",
                "<Schedules nextStageId='2'>"+
                    "<Schedule type='Default'>"+
                        "<stages>"+
                            "<data stageId='1'>"+
                                "<formula id='Microsoft.Office.RecordsManagement.PolicyFeatures.Expiration.Formula.BuiltIn'>"+
                                    "<number>0</number>"+
                                    "<property>ExpirationDate</property>"+
                                    "<propertyId>8c06beca-0777-48f7-91c7-6da68bc07b69</propertyId>"+
                                    "<period>days</period>"+
                                "</formula>"+
                                "<action type='action' id='Microsoft.Office.RecordsManagement.PolicyFeatures.Expiration.Action.Record' />"+
                            "</data>"+
                        "</stages>"+
                    "</Schedule>"+
                "</Schedules>");
                $ctypePolicy.Update();
                $ctype.Update();
            Write-Host "The Policy For" $ctype.Name "Has Been Created And Applied!" -ForegroundColor Green
        }
    }
}

Good Luck!

Dan

Thursday, April 9, 2015

SharePoint 2013: Create Result Sources With PowerShell

Hello Readers!

Sorry for the delay in posts recently, between work and personal events I have been totally swamped (in a good way)!

What I wanted to share today is PowerShell script which provisions result sources at the Site Collection level.  The script will loop through each result source within the script, check to see if it exists, if it does it will blow it away and re-create it or if it is brand new just create   This script is useful for automating deployments or if you need to create the same result sources on multiple site collections.


If you need to deploy result sources at the Search Service Application Level, simply swap out: SPSite with Ssa at our $searchOwner line.  Your site collection URL and Search Service Application are both params which will be prompted.


$searchOwner = New-Object Microsoft.Office.Server.Search.Administration.SearchObjectOwner([Microsoft.Office.Server.Search.Administration.SearchObjectLevel]::SPSite, $site.RootWeb)

The real question though, what do you need to update in order to use this script?  Luckily, the only parts you need to update are the actual result sources themselves!  This is under the Result Sources Start Here Section.  The below example is for a result source which finds anything with the ContentTypeId matching Article.  For the below section I did not need a sort order, but if you do un-comment out the sortCollection and queryProperties line.  You can swap out "Created" with the Managed property you need to sort by.  You can also swap out Descending with Ascending if desired.  The name of the result source is input between the single quotes, my example result source below will be called Articles.


# Articles
Write-Host -ForegroundColor Green "Starting To Build  Articles Result Source"
# define query and sorting
$query = "ContentTypeId:0x010100C568DB52D9D0A14D9B2FDCC96666E9F2007948130EC3DB064584E219954237AF39003F11102A7C0343B0836EDF8DA14F33ED00B679312D96584105BCFD3A33F39BD3AF*"
$queryProperties = New-Object Microsoft.Office.Server.Search.Query.Rules.QueryTransformProperties
$sortCollection = New-Object Microsoft.Office.Server.Search.Query.SortCollection    
##This can be updated if a sort order is required
#$sortCollection.Add("Created", [Microsoft.Office.Server.Search.Query.SortDirection]::Descending) 
#$queryProperties["SortList"] = [Microsoft.Office.Server.Search.Query.SortCollection]$sortCollection
# create result source
CreateResultSource $searchOwner $fedManager ' Articles' $queryProperties $query 
Write-Host -ForegroundColor Yellow " Articles Result Source Complete"

How to use this script:
  1. Update the script to include each result source you need created, you can swap out my example result sources with your own
  2. Run the script
  3. When prompted: input your root site collection url: i.e. http://danstestsite.local
  4. When prompted: input your search service application name (make sure to put it in double quotes) i.e. "Search Service Application"
  5. Check out the new result sources at your site collection level!
Full Script:


## Getting the ducks in a row
param($url = $(Read-Host -prompt "Site Colleciton Root URL"), $ssaName = $(Read-Host -prompt "Search Service Application Name"))

#Funciton To Create Result Sources
function CreateResultSource($searchOwner, $fedManager, $name, $queryProperties, $query)
{  
   # check if result source already exists
   $resultSource = $fedManager.GetSourceByName($name, $searchOwner)

   if ($resultSource -eq $null) 
   {    
        Write-Host -f cyan "Creating Result Source <"$name">."
        
        #create result source
        $resultSource = $fedManager.CreateSource($searchOwner)      
        $resultSource.Name = $name
        $resultSource.CreateQueryTransform($queryProperties, $query)
        $resultSource.ProviderId = $fedManager.ListProviders()["Local SharePoint Provider"].Id
        $resultSource.Commit()
   }
   else
   {
        Write-Host -f Green "Result Source" $name "already exists, updating" $name "result source"

        # print properties of existing result source 
        $source = $fedManager.GetSourceByName($name, $searchOwner) 
        $fedManager.RemoveSource($source)
          #create result source
        $resultSource = $fedManager.CreateSource($searchOwner)      
        $resultSource.Name = $name
        $resultSource.CreateQueryTransform($queryProperties, $query)
        $resultSource.ProviderId = $fedManager.ListProviders()["Local SharePoint Provider"].Id
        $resultSource.Commit()
   }
   return $resultSource
}

Write-Host -ForegroundColor Green "Script Warming Up And Adding Snapins"
Add-PSSnapin "Microsoft.SharePoint.PowerShell" -ErrorAction SilentlyContinue

Start-SPAssignment -Global

#Get SP Site and SP Web
##$url = (Read-Host -Prompt "Site Collection URL")
$url = $url.Trim()
$web = get-spweb $url
$site = get-spsite $url -WarningAction SilentlyContinue

#Get Search Service Applicaiton
$ssa = Get-SPEnterpriseSearchServiceApplication $ssaName -ErrorAction SilentlyContinue
if($ssa -eq $null) {
    $ssa = @(Get-SPEnterpriseSearchServiceApplication)[0]
}

#Output results from SSA
Write-host -ForegroundColor Cyan ("Using search app {0}" -f $ssa.Name)

# load Search assembly
Write-Host -ForegroundColor Green "Loading Search Assemblies"
[void] [Reflection.Assembly]::LoadWithPartialName("Microsoft.Office.Server.Search")

# create manager instances
$fedManager = New-Object Microsoft.Office.Server.Search.Administration.Query.FederationManager($ssa)
    Write-Host -ForegroundColor Green "The fed manager is" $fedManager


#Update SPSite to Ssa if you want them provisioned at Search Service Level
$searchOwner = New-Object Microsoft.Office.Server.Search.Administration.SearchObjectOwner([Microsoft.Office.Server.Search.Administration.SearchObjectLevel]::SPSite, $site.RootWeb)
    Write-Host -ForegroundColor Cyan "The Search Owner is" $searchOwner

###################################################################################
#Result Sources Start Here
###################################################################################


# Articles
Write-Host -ForegroundColor Green "Starting To Build  Articles Result Source"
# define query and sorting
$query = "ContentTypeId:0x010100C568DB52D9D0A14D9B2FDCC96666E9F2007948130EC3DB064584E219954237AF39003F11102A7C0343B0836EDF8DA14F33ED00B679312D96584105BCFD3A33F39BD3AF*"
$queryProperties = New-Object Microsoft.Office.Server.Search.Query.Rules.QueryTransformProperties
$sortCollection = New-Object Microsoft.Office.Server.Search.Query.SortCollection    
##This can be updated if a sort order is required
#$sortCollection.Add("Created", [Microsoft.Office.Server.Search.Query.SortDirection]::Descending) 
#$queryProperties["SortList"] = [Microsoft.Office.Server.Search.Query.SortCollection]$sortCollection
# create result source
CreateResultSource $searchOwner $fedManager ' Articles' $queryProperties $query 
Write-Host -ForegroundColor cyan " Articles Result Source Complete"


#Statistic
Write-Host -ForegroundColor Green "Starting To Build Statistic Result Source"
$query = "ContentTypeId:0x0100AAF9B8EBA36A47F6BB7BA54A5D01B1B8001B7C18A4B6C343C88F4D4293F72F397400ABFD858ADE3B44A0988105611D2187B1*"
$queryProperties = New-Object Microsoft.Office.Server.Search.Query.Rules.QueryTransformProperties
$sortCollection = New-Object Microsoft.Office.Server.Search.Query.SortCollection    
CreateResultSource $searchOwner $fedManager ' Statistics' $queryProperties $query 
Write-Host -ForegroundColor cyan "Statistic Result Source Complete"

#Event Documents
Write-Host -ForegroundColor Green "Starting To Build Call To Event Documents Result Source"
$query = "ContentTypeId:0x01010069411AE48AE74A3096A71D0C77BC6CBD* AND ManagedTerm:{Term.Id}"
$queryProperties = New-Object Microsoft.Office.Server.Search.Query.Rules.QueryTransformProperties
$sortCollection = New-Object Microsoft.Office.Server.Search.Query.SortCollection    
CreateResultSource $searchOwner $fedManager 'Event Documents' $queryProperties $query 
Write-Host -ForegroundColor cyan "Event Documents Result Source Complete"

#Event Images
Write-Host -ForegroundColor Green "Starting To Build Event Images Result Source"
$query = "ContentTypeId:0x010102002042E861E3D14C70B595912F16571C8C* AND ManagedTerm:{Term.Id}"
$queryProperties = New-Object Microsoft.Office.Server.Search.Query.Rules.QueryTransformProperties
$sortCollection = New-Object Microsoft.Office.Server.Search.Query.SortCollection    
CreateResultSource $searchOwner $fedManager 'Event Images' $queryProperties $query 
Write-Host -ForegroundColor cyan "Event Images Result Source Complete"



###################################################################################
#Result Sources End Here
###################################################################################
Well that is really it folks, hopefully this helps to automate some deployments or at the very least was an interesting read! Dan

Monday, January 5, 2015

Date Slider Refiner (slider with bar graph) Not Working for a Custom Managed Date Property!

I am assuming if you are reading this article you have hit the frustrating wall of your custom date refiner showing the graph and slider incorrectly.  The refiner looks correct until you actually use the refiner and realize that range is numeric instead of last year, six months, etc!

Well if you are looking for a fix you are in the right spot friend.  The refiner is not working correctly due to your managed property name!  Believe it or not the managed property name will impact the date slider refiner.

For this example I have created a new managed property of date type called date_Published which has an associated crawled property and all of the correct settings.  When I modify my refinement panel and add date_Published as a refiner using the slider with bar graph, it looks correct but the values are all numeric integers!

After a great deal of sleuthing I realized that if you append on two numbers to the end of your managed property between 0 – 9 the property will render correctly, aka managed_PropName[0-9][0-9].  When I updated my managed property to the name date_Published85 the refiner rendered correctly!

Final Results


Hope This Helps!

Dan

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.

<body>
    <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(!useWACUrl)
        {
            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;
        if($isEmptyString(url)){
            if(useWACUrl)
            {
                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="this.style.display='inline'" src="_#= $urlHtmlEncode(ctx.CurrentItem.csr_Icon) =#_" />
                </div>
<!--#_
            }

                                          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 =#_
                </h3>
            </div>
<!--#_ 
            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 =#_
            </div>
        </div>
      


<!--#_
        if (!$isEmptyString(ctx.CurrentItem.csr_PreviewImage)) 
        {
            var altText = Srch.Res.item_Alt_Preview;
            if(!$isEmptyString(ctx.CurrentItem.csr_PreviewImageAltText)){
                altText = ctx.CurrentItem.csr_PreviewImageAltText;
            }

            var onloadJS = "var container = $get('" + $scriptEncode(id + Srch.U.Ids.preview) + "'); if(container){container.style.display = '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 =#_
            </div>
<!--#_
        }
         for (var p in ctx.CurrentItem)
  {
       console.log(p + ":" + $getItemValue(ctx, p));
  }
  
         _#-->
    </div>
</body>
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!
Dan

Tuesday, July 22, 2014

Setting Up Your First Device Channel in SharePoint 2013

Hello!

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
    • Whatsmyuseragent.com
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 www.whatsmyuseragent.com 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


Cheers!
Dan

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;
}
 </script>

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,
Dan

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>
      </
      SharePoint:SPSecurityTrimmedControl>
  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"
</span
</SharePoint:SPSecurityTrimmedControl>

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!


Dan

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.


Assumptions:
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.

Cheers!
Dan

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”)
 {
 x.item(i).style.display=”none”;
 }
 }
 </script>


Cheers!
Dan