Monday, May 26, 2014

The solution cannot be deployed. The feature 'some-feature-id' uses the directory "some-directory" in the solution

I recently came across this error when trying to deploy a WSP:
The solution cannot be deployed.  The feature 'some-feature-id' uses the directory "some-directory" in the solution. However, it is currently installed in the farm to the directory "some-other-directory". Uninstall the existing feature before you install a new version of the solution.

Simple enough, I tried to do an Uninstall-SPFeature as well as Uninstall-SPFeature -Force both of which did not help. Finally someone tried stsadm -o uninstallfeature and then the subsequent WSP deploy worked. I guess Powershell and stsadm are not quite equivalent


Calling private member using reflection in Powershell

Often I have to put together some Powershell scripts for troubleshooting. These scripts are based on some c# code. Recently, I had to mimic the behaviour of a call to a private member overload of GetTags in the Microsoft.Office.Server.SocialData.SocialTagManager class.

$site = Get-SPSite my-site-url
$context = Get-SPServiceContext($site)
$socialTagManager = New-Object -TypeName Microsoft.Office.server.SocialData.SocialTagManager -ArgumentList $context

# Get the type definition
$type = [type]'Microsoft.Office.Server.SocialData.SocialTagManager'

# List the GetTags members
($socialTagManager | Get-Member GetTags).Definition.Replace("), ", ")`n")

This results in (just the public members):
Microsoft.Office.Server.SocialData.SocialTag[] GetTags(Microsoft.Office.Server.UserProfiles.UserProfile user)
Microsoft.Office.Server.SocialData.SocialTag[] GetTags(Microsoft.Office.Server.UserProfiles.UserProfile user, int maximumItemsToReturn)
Microsoft.Office.Server.SocialData.SocialTag[] GetTags(Microsoft.Office.Server.UserProfiles.UserProfile user, int maximumItemsToReturn, int startIndex)
Microsoft.Office.Server.SocialData.SocialTag[] GetTags(System.Uri url)


Now use Reflection

# Which methods to list
$bindingFlags = [Reflection.BindingFlags] "Default,NonPublic,Instance"

# List the methods
$type.GetMethods($bindingFlags) | Where-Object {$_.Name -eq 'GetTags'} | ForEach-Object {$_.GetParameters() | Select-Object Member -First 1}

This results in (both public and private members);
Microsoft.Office.Server.SocialData.SocialTag[] GetTags(System.DateTime, System.DateTime)
Microsoft.Office.Server.SocialData.SocialTag[] GetTags(System.DateTime, System.DateTime, Int32)
Microsoft.Office.Server.SocialData.SocialTag[] GetTags(System.DateTime, System.DateTime, Int32, Int32)
Microsoft.Office.Server.SocialData.SocialTag[] GetTags(System.Uri, Microsoft.Office.Server.UserProfiles.UserProfile, System.Nullable`1[System.DateTime], System.Nullable`1[System.DateTime])
Microsoft.Office.Server.SocialData.SocialTag[] GetTags(System.Uri, Int32)
Microsoft.Office.Server.SocialData.SocialTag[] GetTags(System.Uri, Int32, Microsoft.Office.Server.SocialData.SocialItemPrivacy)
Microsoft.Office.Server.SocialData.SocialTag[] GetTags(Microsoft.SharePoint.Taxonomy.Term[], Int32, Microsoft.Office.Server.SocialData.SocialItemPrivacy)
Microsoft.Office.Server.SocialData.SocialTag[] GetTags(Microsoft.Office.Server.UserProfiles.UserProfile, Int32, Int32, Boolean, System.Nullable`1[System.DateTime], System.Nullable`1[System.DateTime])
Microsoft.Office.Server.SocialData.SocialTag[] GetTags(Microsoft.Office.Server.UserProfiles.UserProfile, Int32, Int32, Boolean, System.Nullable`1[System.DateTime], System.Nullable`1[System.DateTime], System.Nullable`1[System.Guid])


Now to call a private member. Let's call: Microsoft.Office.Server.SocialData.SocialTag[] GetTags(System.DateTime, System.DateTime, Int32, Int32)


$startTime = [DateTime]'2014-05-01 00:00:00'
$endTime = [DateTime]'2014-05-31 00:00:00'
$maximumItemsToReturn = 1000
$startIndex = 0
$typeList = @([type]'DateTime', [type]'DateTime', [type]'Int32', [type]'Int32')
$method = $socialTagManager.GetType().GetMethod('GetTags', $bindingFlags, $null, $typeList, $null)
$terms = $method.Invoke($socialTagManager, [Object[]] @($startTime, $endTime, $maximumItemsToReturn, $startIndex))
$terms | Format-Table @{l='Term';e={$_.Term.Name}}, Title -AutoSize




Sunday, May 25, 2014

Get all features that define Site Settings links

Recently, I needed to find out where a particular link listed under Site Settings. Doing a bit of research on how to add links to a Site Settings page involves defining a CustomAction in the Elements.xml file with a Location of Microsoft.SharePoint.SiteSettings. Reversing this logic, we can find all Elements.xml files that contains a line Location="Microsoft.SharePoint.SiteSettings" and from there work backwards to get the SharePoint feature.

Here is a Powershell that does this:
Get-ChildItem -Recurse -Filter *.xml | Select-String -Pattern 'Microsoft.SharePoint.SiteSettings' | Group-Object path | ForEach-Object {$x = $_.Name; Get-SPFeature | Where-Object {$x -like "$($_.RootDirectory)*"}} | Format-Table Id, Scope, @{l='Title';e={$_.GetTitle(1033)}} -AutoSize

This script can easily be modified to find other menu items by changing the Select-String -Pattern to something else such as Microsoft.SharePoint.StandardMenu.