Wiping Taxonomy Values

Ever want to wipe out Taxonomy values in a Managed Metadata field in a library? Assigning a null won’t cut it. My good friend Brett Parker found the solution:

            for ($i=0; $i -lt $count; $i++)
            {
                  $doc = $items[$i]
                  $field = [Microsoft.SharePoint.Taxonomy.TaxonomyField]$lib.Fields[$CompanyMyGroup]
                  $empty = New-Object Microsoft.SharePoint.Taxonomy.TaxonomyFieldValue($field)
                  $field.SetFieldValue($doc, $empty)
                  $doc.SystemUpdate()
            }

Timer Job History

SharePoint maintains a timer job history by default for seven days. You can see the current duration:

$TJ = Get-SPTimerJob | Where-Object {$_.name -eq "job-delete-job-history"}
$TJ.daystokeephistory

If you’d like to know when the job last ran just run:

$TJ.get_LastRunTime()

If you want to reduce the days to retain history, here’s how:

$TJ.set_DaysToKeepHistory(3)
$TJ.update()

I suggest changing the cleanup frequency to run daily, to reduce periodic growth.

Occasionally the Timer Job History grows to the point where it needs to be gradually cleaned. You can use the technique I outline in this article to gradually cull the timer job history:
http://reality-tech.com/2012/03/28/sync-db-grows-indefinitely/

 

You can also review Timer Job history via SQL against the farm DB:

SELECT TOP 1000

[Status]
 ,[StartTime]
 ,[EndTime]
 ,[WebApplicationName]
 ,[JobTitle]
 ,[ServerName]
 ,[DatabaseName]
 ,[ErrorMessage]
 FROM [SharePoint_2010_Farm].[dbo].[TimerJobHistory]

 where CHARINDEX('text you know is in the job name',JobTitle) > 0

Search Crawling in SP2013

In SP2010, we have two types of crawls; Full or Incremental Crawl. In a nutshell, your search index can be made on-average fresher, but it is not real-time. Right now we do Full Crawls weekly and incremental crawls hourly.

One of the limitations of Full and Incremental Crawls in SP2010 is that they cannot run in parallel, i.e. if a full or incremental crawl is in progress, the admin cannot kick-off another crawl on that content source. This forces a first-in-first-out approach to how items are indexed.

Moreover, some types of changes result in extended run times; such as script based permission changes, or moving a folder, or changing fields in a content type. Incremental crawls don’t remove “deletes”, so ghost documents are still returned as hits, after deletion, until the next full crawl.

SharePoint 2013 will introduce the concept of “Continuous Crawl”. It doesn’t need scheduling. The underlying architecture is designed to ensure consistent freshness by running in parallel. Right now, if a Full or Incremental crawl is slow, everything else awaits its completion. It’s a sequential crawl. Behind the scenes, continuous crawl selection results in the kick-off of a crawl every 15minutes (this wait can be configured) regardless of whether the prior session has completed or not. This means a change that is made immediately after a deep and wide-ranging change doesn’t need to ‘wait’ behind it. New changes will continue to be processed in parallel as a deep policy change is being worked on by another continuous crawl session.

Note that Continuous crawl will increase load incrementally on the SharePoint server since it inherently can run parallel multiple sessions simultaneously. If needed, we can tune this through ‘Crawl Impact Rule’ settings (which exist today in SP2010) which controls the maximum number of simultaneous requests that can be made to a host (default is 12 threads, but is configurable).

Hope this is useful.

Joel

When users can’t access a taxonomy

The Managed Metadata Service is great; but what do you do when users can’t view some taxonomy entries? This occurs when the user does not have access to the HiddenTaxonomyList, native to each Site Collection.

You can view the list using SharePoint Manager from CodePlex, navigating the Object Model, or simply by modifying this URL to reflect your site collection URL:
http://SharePoint/sites/SiteCollection/Lists/TaxonomyHiddenList/AllItems.aspx where “http://SharePoint/sites/SiteCollection” is replaced with your site collection URL.

I recently found a situation where permissions by default were empty. Best would be to allocate all Authenticated users access. However what does one do if there are many site collections within a given Web Application? Here’s a script that will iterate through Site Collections, and grant the access:

$WebApp = "http://SharePoint" #replace with your own web app
$webapp = get-spwebapplication $webapp

function AddPerm ([Microsoft.SharePoint.SPList] $TargetObj, [string] $RoleValue, [string] $RoleGroup)
{ #SPWeb is implied and not passed as parms for efficiency!
	if ((!$RoleValue) -or (!$RoleGroup))
	{
	return; #race to be efficient on NullOp
	}
	try
	{
				$user = $SPWeb.ensureuser($RoleGroup)
				$roledef = $SPWeb.RoleDefinitions[$RoleValue]
				$roleass = New-Object Microsoft.SharePoint.SPRoleAssignment($user)
				$roleass.RoleDefinitionBindings.Add($roledef)

				$TargetObj.RoleAssignments.Add($roleass)  
	}
	catch
	{
	Write-Host -ForegroundColor DarkRed "ERR: Can't Assign $($RoleGroup)"
	}
}

for ($i=0; $i -lt $WebApp.Sites.Count; $i++)
{
	$site=$webapp.Sites[$i];
	$SPWeb=$site.rootweb;
	$list = $SPWeb.Lists["TaxonomyHiddenList"]
	addPerm $list "Read" "SharePoint\NT Authenticated Users"
}

Send email from PowerShell

It’s easy to send an email from PowerShell, and it’s really useful for notifying yourself of the completion of a long-running script. It’s also a way to document what ran when, if you are like me and running hundreds of scripts and need a way to organize documentation on when you run things. Just alter the parameters below, and let ‘er rip and spam yourself!

param(  
        [string] $From = "server@joelDomain.com",
        [string] $To = "joelplaut@joelDomain.com",
        [string] $Title = "title",
        [string] $Body = "body"
    )
    $SmtpClient = New-Object System.Net.Mail.SmtpClient
    $SmtpServer = "mail.domainserver.com"
    $SmtpClient.host = $SmtpServer 
    $SmtpClient.Send($From,$To,$Title,$Body)