Author Archives: Merill Fernando

Latest build of Windows 10 removes ‘Smart Files’. Go vote to get it back

Smart files is one of the most awesomest feature in Windows 8. It basically lets you ‘see’ all your files in OneDrive but takes up a ridiculously minute fraction of storage. When you want to work on a file, either through an stop or double clicking, out would auto-magically pull down the whole file. See more here http://gcoupe.wordpress.com/2014/11/13/build-9879-of-windows-10-has-removed-smart-files/

In the latest build of the Windows 10 Tech Preview this feature had been removed. What’s refreshing about the new Microsoft is that we can vote to get it back. Go add your vote here. https://windows.uservoice.com/forums/265757-windows-feature-suggestions/suggestions/6708195-add-an-advanced-option-to-restore-showing-all-oned

You can also enjoy the passionate feedback from users.

Why I’m strongly considering temporarily moving to an iPhone till Windows Phone catches up

Why I’m strongly considering temporarily moving to an iPhone till Windows Phone catches up:
Just got an email that my work phone has been moved to Jabber. Cisco says Jabber can be used from any device, they have iOS and Android apps but nothing for Windows Phone. Long story short the fault lies with Windows Phone.

This was posted in the forum last month by a Cisco rep: ‘Hi Brian,
The open sockets we need to have running in the background are currently not supported by Windows Phone 8.1. The Windows Phone development group has been discussing with us to ensure that they can support Jabber architecture in the Threshold release.’

https://communities.cisco.com/thread/31390?start=15&tstart=0

The Xbox Music app is the bane of my existence. I am an Xbox music subscriber and it is absolutely pathetic for a native app. This article (dated yesterday) and the comments thread sums it all up. http://wmpoweruser.com/xbox-music-still-sucks-and-thats-a-sh…

And then you see benchmarks like this http://www.anandtech.com/show/8666/the-apple-ipad-air-2-revi… (also http://blog.codinghorror.com/the-tablet-turning-point/) with the iPhone 6 & iPad Air beating out all other devices, providing a superior app store selection.

Even the new the Microsoft Office app that Microsoft released for iOS is much better at rendering and more feature rich than the native Office apps in Windows Phone (which have been native and haven’t been update since WP 7,0).

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

Tweaking the default search display template in SharePoint 2013

Let’s look at what it takes to make a minor tweak to the out of the box search result in SharePoint 2013. Say my requirement is to show the document id in the search result like the sample shown below.

Search Result

For the example above I’m going to create a custom display template for PowerPoint files.

Let’s start by mapping the display templates folder as a local drive. Browse to /_layouts/15/DesignMapDrive.aspx and click on the /_catalogs/masterpage/ link to open the drive. You can then map that as a network drive.

Through explorer browser to the \_catalogs\masterpage\Display Templates\Search folder and copy these two files file to a local drive.

  • Item_PowerPoint.html
  • Item_CommonItem_Body.html

Rename Item_PowerPoint.html to make it your own.

Open up the file and change the <title> node. This is what get’s displayed in SharePoint.

Make the changes highlighted below.

I’m basically updating the title in javascript to show the doc id. You would think this would be enough, unfortunately the render body method doesn’t seem to pick up the updated value. Instead you need to copy the entire div from the Item_CommonItem_Body.html file. Copy the Item_CommonItem_Body div from that file into your customised PowerPoint file and overwrite the line that makes the call to ctx.RenderBody.

DisplayTemplate

Upload your custom html file to Search display templates mapped folder.

Now browse to the Manage Result Types page from Site Collection Administration and make a copy of the Microsoft PowerPoint result type.

From the Actions drop down pick the display template that you just uploaded and your all set.

Things to watch out for when migrating SharePoint 2010 FAST Search to SharePoint 2013

I’m currently working on a Search First migration to SharePoint 2013. This post is a placeholder to document the gotchas and workarounds.

Search Centre Keywords

Our initial plan was for a search-first migration where we would swap out the FAST search service application for an SP2013 search service application. The idea was that we would make no changes to the existing search centres but they would get a better/fast search engine with all the improvements that come in 2013.

Issue: Keywords defined in the SP 2010 Search Centre are ignored by SP2013 Search. The only way this functionality can work is when you use a SP2013 search centre. This is not a problem if you have only one search centre since you can manage the keywords at a farm level but we had a number of search centres with site collection admins needing the ability to manage the keywords for the site they manage.

Workaround: We were forced to create Search Centres in SP 2013 and have the SP 2010 sites use the new search centres.

XSLT to Display Templates

Given that we were on the path to using SP2013 search centres any customisations that were made to the SP2010/FAST search XSL now needs to be re-written as Display Templates. In a way this is better than having to mess with XSL, but this is still something that you need to take into account.

 

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.