Category Archives: Tips

Script to Download Yammer Image Profile

Here’s a neat script I wrote the other day to download the urls of all the user’s in your Yammer network. This comes in handy when you want to re-use the profile images that users have uploaded to Yammer.

The advantage with this script is that you don’t need the app to be approved by your Yammer admin. Just create a user created app on Yammer, create a token and approve it for your profile. That’s it. The token can be re-used as long as necessary.

Here’s the PowerShell script that will get you there.

<#
.SYNOPSIS
    This script downloads the profile image urls of all the users from Yammer, creates a csv file and uploads it to the YammerProfile document library.
    Example>    .\Download-YammerProfile.ps1 -YammerToken 05fdIb1pNwIFEBg9k34EWw -SiteUrl http://mysharepoint/site
#>
param(
    [Parameter(Mandatory = $True, HelpMessage='Yammer Access Token - Refer to https://developer.yammer.com/authentication/ for how to generate an access token. Eg. 05fdIb1pNwIFEBg9k34EWw')]
    [string]$YammerToken,
    [Parameter(Mandatory = $True, HelpMessage='The url of the site that will store the Yammer user profile list. E.g. http://mysharepoint/site')]
    [string]$SiteUrl
)

Function Main() {
    $documentLibrary = "YammerProfiles"
    $csvFile = "YammerProfiles.csv"

    CreateEventLogSource "MePage"
    Log "Starting Download-YammerProfile.ps1"
    try {
        $spWeb = Get-SPWeb -Identity $SiteUrl
        CreateSharePointLibrary $spWeb $documentLibrary  "Downloaded cache of Yammer profile image urls" "DocumentLibrary"
        CreateYammerProfileCsv $YammerToken $csvFile
        $spWeb = Get-SPWeb -Identity $SiteUrl #Refresh reference to avoid timeout issue when it takes a long time to download from Yammer
        UploadToSharePoint $csvFile $spWeb $documentLibrary

        Log "Download-YammerProfile.ps1 completed succesfully"
    }
    catch {
        [string]$errDetail = $_.ToString()
        LogError "Error occured: $errDetail"
        Write-Error $_
    }
}

Function CreateYammerProfileCsv($token, $csvFile){
    $page = 0
    $userCount = 0
    if (Test-Path $csvFile){ Remove-Item $csvFile }
    do {
        $page++     
        $users = GetYammerUsers $token $page
        $userCount = $users.Length
        Log "Downloading Yammer Profiles Page: $page Users: $userCount"
        for($i = 1; $i -lt $users.Length; $i++) {
            $user = $users[$i];         
            if($user.mugshot_url_template.EndsWith("no_photo.png")) { continue } #Only store valid photos           
            for($e = 0; $e -lt $user.contact.email_addresses.Length; $e++) {                
                $email = $user.contact.email_addresses[$e];
                if($email.type -eq "primary"){ #We're only interested in the telstra.com email addresses    
                    $output = $email.address.ToLowerInvariant() + "," + $user.mugshot_url_template
                    Write-Host $user.mugshot_url_template $email.address
                    Add-Content $csvFile $output
                }
            }
        }
        Start-Sleep -s 2 #Sleep for two seconds so we don't go over Yammer's rate count of 10 req / 10 sec
    }
    while ($userCount -gt 0) # -and $page -eq 1)
}

Function UploadToSharePoint($fileName, $spWeb, $documentLibraryName) {
    Log "Uploading $fileName to $spWeb/$documentLibraryName"
    #Read the contents into a memory stream to prevent the file getting locked
    [byte[]]$byteArray = Get-Content -encoding byte -path $fileName 
    [void][reflection.assembly]::LoadWithPartialName("System.IO")
    $stream = [System.IO.MemoryStream]$byteArray

    $spDoc = $spWeb.GetFolder($documentLibraryName)
    $spFile = $spDoc.Files.Add($fileName, $stream, $true)
}

Function CreateSharePointLibrary ($Web, $LibraryName, $Description, $LibraryTemplate) {  
    $spListCollection = $spWeb.Lists   
    $spLibrary=$spWeb.Lists.TryGetList($LibraryName)
    if($spLibrary -eq $null) {
        Log -f Yellow "$LibraryName document library does not exist. Creating..."
        $spListCollection.Add($LibraryName, $Description, $LibraryTemplate)
    }
}

Function LoadPSSnapIn {
    $snapin = Get-PSSnapin | Where-Object {$_.Name -eq 'Microsoft.SharePoint.Powershell'}
    if ($snapin -eq $null) {
        Write-Host "Loading SharePoint Powershell Snapin"
        Add-PSSnapin "Microsoft.SharePoint.Powershell"
    }
}

Function ConvertToJson($jsonString) {
    [System.Reflection.Assembly]::LoadWithPartialName("System.Web.Extensions")
    $ser = New-Object System.Web.Script.Serialization.JavaScriptSerializer
    return $ser.DeserializeObject($jsonString)
}

Function GetYammerUsers($token, $pageNumber) {
    $users = InvokeYammerGetRequest $token "users.json?page=$pageNumber"
    return $users
}

Function InvokeYammerGetRequest($token, $target) { 
    # Setup the request
    $yammerApiUrl = "https://www.yammer.com/api/v1/"
    $requestUrl = $yammerApiUrl + $target
    $webRequest = [System.Net.WebRequest]::Create($requestUrl)
    $webRequest.Method = "GET"
    $webRequest.Headers.Add("Authorization", "Bearer $token")

    # Execute the request
    [System.Net.WebResponse]$resp = $webRequest.GetResponse()
    $reqstream = $resp.GetResponseStream()
    $sr = new-object System.IO.StreamReader $reqstream
    $obj = ConvertToJson $sr.ReadToEnd()
    return $obj
}

Function CreateEventLogSource($sourceName) {            
    try {
        if(![System.Diagnostics.Eventlog]::SourceExists($sourceName))
        {
            Write-Host "Creating event log source $sourceName"
            New-Eventlog -LogName "Application" -Source $sourceName
        }
    }
    catch {
        Write-Host "Error creating event log source $sourceName"            
        Write-Host $_
    }
}

Function Log($message) {
    Write-Host $message
    WriteEventLog $message $false
}

Function LogError($message) {   
    WriteEventLog $message $true    
}

Function WriteEventLog($message, $isError) {
    try {
        if($isError) {
            Write-EventLog -logname Application -source MePage -eventid 4002 -EntryType Error -Message $message
        }
        else {
            Write-EventLog -logname Application -source MePage -eventid 4001 -EntryType Information -Message $message
        }
    }
    catch { #We don't want the script to error out because it couldn't write to event log       
        Write-Host "Error writing message to eventlog. Message = $message"
        Write-Host $_
    }
}

$ErrorActionPreference = "stop"
LoadPSSnapIn
return Main

Creating PDF files dynamically with PowerShell

So how does one go about creating PDF files dynamically? You need a pinch of the open source PdfSharp library and 10 lines of PowerShell.

Add-Type -Path .\PdfSharp.dll
$doc = New-Object PdfSharp.Pdf.PdfDocument
$doc.Info.Title = "Created dynamically"
$page = $doc.AddPage()
$gfx = [PdfSharp.Drawing.XGraphics]::FromPdfPage($page)
$font = New-Object PdfSharp.Drawing.XFont("Verdana", 20, [PdfSharp.Drawing.XFontStyle]::BoldItalic)
$msg = "Hello World"
$rect = New-Object PdfSharp.Drawing.XRect(0,0,$page.Width, $page.Height)
$gfx.DrawString($msg, $font, [PdfSharp.Drawing.XBrushes]::Black, $rect, [PdfSharp.Drawing.XStringFormats]::Center)
$doc.Save("HelloWorld.pdf")

Creating jUnit/xUnit compatible xml test tesults in PowerShell

I originally wrote this function when I needed to create jUnit/xUnit compatible test results. The resulting xml file was then fed into the jUnit plugin in Jenkins to show the results PowerShell/PSUnit based tests.

Function Write-JunitXml([System.Collections.ArrayList] $Results, [System.Collections.HashTable] $HeaderData, [System.Collections.HashTable] $Statistics, $ResultFilePath)
{
$template = @'
<testsuite name="" file="">
<testcase classname="" name="" time="">
    <failure type=""></failure>
</testcase>
</testsuite>
'@

    $guid = [System.Guid]::NewGuid().ToString("N")
    $templatePath = [System.IO.Path]::Combine([System.IO.Path]::GetTempPath(), $guid + ".txt");

    $template | Out-File $templatePath -encoding UTF8
    # load template into XML object
    $xml = New-Object xml
    $xml.Load($templatePath)
    # grab template user
    $newTestCaseTemplate = (@($xml.testsuite.testcase)[0]).Clone()  

    $className = [System.IO.Path]::GetFileNameWithoutExtension($HeaderData.TestFileName)
    $xml.testsuite.name = $className
    $xml.testsuite.file = $HeaderData.TestFileName

    foreach($result in $Results) 
    {   
        $newTestCase = $newTestCaseTemplate.clone()
        $newTestCase.classname = $className
        $newTestCase.name = $result.Test.ToString()
        $newTestCase.time = $result.Time.ToString()
        if($result.Result -eq "PASS")
        {   #Remove the failure node
            $newTestCase.RemoveChild($newTestCase.ChildNodes[0]) | Out-Null
        }
        else
        {
            $newTestCase.failure.InnerText = Format-ErrorRecord $result.Reason
        }
        $xml.testsuite.AppendChild($newTestCase) > $null
    }   

    # remove users with undefined name (remove template)
    $xml.testsuite.testcase | Where-Object { $_.Name -eq "" } | ForEach-Object  { [void]$xml.testsuite.RemoveChild($_) }
    # save xml to file
    Write-Host "Path" $ResultFilePath

    $xml.Save($ResultFilePath)

    Remove-Item $templatePath #clean up
}

Debugging Javascript

What’s the easiest way to break into the debugger in JavaScript especially when you have heaps of js files being loaded by a page (SharePoint 2013 & display templates, I’m looking at you)
Apparently this is a very old trick (pre 2006). Just add the debugger command (and enable script debugging if your using IE).

debug

Link

XPathTool is a really great online XPath utility. It’s free, doesn’t need to be installed and has more features than a lot of the utilities that you find on CodePlex.

Configure the Belkin Play Max router for SLT ADSL Broadband

I’m on vacation back home in Sri Lanka and became the tech support guy for the in-laws. They were using an old D-Link router which supported just 802.11b and a draft version of g. In short ancient. The house has two stories and a terrace but the wifi wouldn’t even work on all the rooms on the same floor.

So I went to Unity plaza and checked out all the routers that are available. Unfortunately there’s not much in terms of choice. All of the available routers were the low end versions by D-Link, a few have a low-end Linksys router and some Belkin routers. You also get a handful of Asian brands of which the Unity Plaza sales guys tout the TP-Link as being the best brand, far superior to D-Link, Linksys etc.

I finally settled on the Belkin Play Max N300+300 which sells here for Rs. 16,500/-

I got home plugged it in but couldn’t get it to connect to the SLT ADSL. I spent a few hours trying various settings, calling SLT to verify the username/password all to no avail. I finally updated the router from v1.00.45 to v1.00.46 and viola it worked.

The connectivity is really good and I don’t have any dead spots in the house.

Here are a few screenshots of the config page for SLT’s settings.

Limit SQL Server memory usage on your workstation, laptop or VM

Here’s a neat tip I learnt over the weekend.

All SQL Server instances are by default set up to use all the memory available on your workstation.

This is ideal when you have SQL Server running on it’s own dedicated server, not so ideal when you have SQL Server installed on your laptop, workstation or even on a SharePoint VM.

Here’s what MSDN says

if SQL Server is one of several server applications running on a single computer, the system administrators may need to control the amount of memory allocated to SQL Server. In these cases, you can use the min server memory and max server memory options to control how much memory SQL Server can use.

In the Server Memory Options page they go on to say:

When you are running multiple instances of the Database Engine, there are three approaches you can use to manage memory

  • Use max server memory to control memory usage.
  • Use min server memory to control memory usage.
  • Do nothing (not recommended).

Which brings us to how we can set the maximum limit. Quite easy. Just connect to each SQL Server instance and set the maximum memory to a more palatable value.

Here’s a visual walk through to limit the maximum memory usage to 512MB for your SharePoint 2010 instance (if you installed it on Windows 7).

1. Start SQL Server Management Studio (or SSMS Express) and connect to your SQL Server instance (SharePoint in this case): localhost\SharePoint

2. Right-click on the instance node and select Properties.

3. Click on the Memory node you’ll notice that the Maximum Server Memory is set to 2,147,483,647MB change it to a lower limit like 256 or 512MB. Click OK and your all set.

If you prefer SQL the same can be done with the following commands.


Enable advanced options:

USE master

EXEC sp_configure 'show advanced options', 1

RECONFIGURE WITH OVERRIDE


Set the maximum amount of memory to 512 MB:

USE master

EXEC sp_configure 'max server memory (MB)', 512

RECONFIGURE WITH OVERRIDE


Display the newly set configuration:

USE master

EXEC sp_configure 'max server memory (MB)'


Set ‘show advanced options’ back to default:

USE master

EXEC sp_configure 'show advanced options', 0

RECONFIGURE WITH OVERRIDE

Visual Studio 2010 Installation Issue – Setup Failed with HRESULT -2147467259

If you Visual Studio 2010 setup keeps failing when it tries to install the Visual C++ runtime, here’s a quick fix for you.

Try installing one of the Visual Studio 2010 Express Editions (I did the Web Edition: http://www.microsoft.com/web/downloads/platform.aspx) and then run the VS2010 installation.

I think it has something to do with mounting the ISO as a drive.