At times I create so many sites and sub-sites that the Top-Nav cannot handle it. It is better to add the new links to the Quick Launch.
The function below makes it a snap.
Here is how it is used:
The first line inserts a heading "OrgSites" under the "Lists" heading (already there) in the QuickLaunch.
New-SPNode -Web $siteurl
-NodeText "OrgSites" -NodeLink "Head"
-NodeParent "Lists"
TAnd this line inserts the URL of a site named $web1name and the URL $web1url unter the new "OrgSites" heading
New-SPNode -Web $siteurl -NodeText $web1name -NodeLink $web1url -NodeParent "OrgSites"
Here (FINALLY!!) is the function
# New-SPNode creates an entry in
the QuickLaunch navigation
# $Web is the URL of the site
# A node is made of text -
$NodeText and link - $NodeLink
# If the link says
"Head", then the node is a heading, else it is assumed a URL
# If "Head" then the
heading is added immediately under The parent heading ($NodeParent)
# No checking for a URL being a
URL
# Nodes are always added last
# Nodes are added under
$NodeParent
function New-SPNode {
[CmdletBinding()]
Param(
[Parameter(Mandatory=$true,ValueFromPipeline=$true)]
[string]$Web,
[Parameter(Mandatory=$true)]
[string]$NodeText, #"Head" if Heading
[Parameter(Mandatory=$true)]
[string]$NodeLink,
[Parameter(Mandatory=$true)]
[string]$NodeParent
)
Start-SPAssignment -Global
$SPWeb = Get-SPWeb
-Identity $Web
$QL = $SPWeb.Navigation.QuickLaunch
#
QuickLaunch
$PrevHead = $QL | where {$_.Title -eq $NodeParent}
# Check if heading
If ($NodeLink -eq "Head")
{
$NewHead = New-Object Microsoft.SharePoint.Navigation.SPNavigationNode($NodeText, "") $QL.Add($NewHead, $PrevHead) | Out-Null
}
else
{
$NewNode = New-Object Microsoft.SharePoint.Navigation.SPNavigationNode($NodeText, $NodeLink) Write-Host "New-SPNode:
" $Web " " $NodeText " " $NodeParent -ForegroundColor Blue -Backgroundcolor magenta
$PrevHead.Children.AddAsLast($NewNode) | Out-Null
}
$SPWeb.Dispose()
Stop-SPAssignment -Global
}
That's All Folks!!
Doug Finke, A PowerShell MVP tested it in .Net where it fails as well. He explained that The call is ambiguous between the following methods or properties: 'System.Math.Floor(decimal)' and 'System.Math.Floor(double)'
My understanding is that PowerShell convert variables as it "pleases". 1/5 will most likely convert to float.
There maybe another approach to doing it w/o the [Math]::Floor. It is the format it with "{0:N0}", then use the D2 format.
$i = 1
$j = "{0:N0}" -f $i/5
$k = "{0:D2}" -f $j
AND IT WORKS!! This is probably better than the [Math]::Floor
That’s All Folks
Here is a piece of code I tested for the conversion of the numbers 0 thru 99 into 20 strings of exactly 2 digits. The first is 04, the next 09, then 14,19,24,29, etc. all the way to 94, 99.
The code: "{0:D2}" -f $d formats each number to exactly 2 digits.
The $i used in the “for” loop is interpreted by PowerShell – it automatically assigns it to a float. So when dividing by 5, we get fractions. I needed it to be whole numbers, hence the [math]::floor($i/5).
cls
for ($i = 0; $i -lt 100; $i++)
{
$n = [math]::floor($i/5)
$d = $n * 5 + 4
"{0:D2}" -f $d
}
I erroneously assumed that the result $d will be formattable ("{0:D2}" -f $i renders the desired result), alas this results in the following error:
Error formatting a string: Format specifier was invalid..
At E:\HA\testformat.ps1:6 char:2
+ "{0:D2}" -f $d
+ ~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: ({0:D2}:String) [], RuntimeException
+ FullyQualifiedErrorId : FormatError
I tried all sorts of things, but the error was persistent, then I tried coercing – the PowerShell term for casting. I used [float] and [decimal] to no avail, but my 3rd (and obviously last*) attempt was [int] and it worked.
cls
for ($i = 0; $i -lt 100; $i++)
{
$n = [math]::floor($i/5)
[int]$d = $n * 5 + 4
"{0:D2}" -f $d
}
Generated the desired results.
* Why is it that we always find what we look for in the VERY LAST place we looked?
That’s all Folks
Comparing WFE servers
One of the SharePoint farms I am involved with has a load balanced pair of WFE. One of the web parts had acted sporadically – at times working well, at others not at all and I suspected that it might be because it was set wrongly on one of the servers and correctly on the other. Well, I went and checked the GACs on both servers. I did this by writing a PowerShell script that garners the assemblies and outputs them into a spreadsheet. Now with two spreadsheets in my hand I proceeded to examine them for differences.
NB the item count itself it was obvious that the twain were not similar, but I needed detail and with hundreds of items in each and but a pair of eyes – tired eyes – the task was daunting.
Well, I obviously assumed that like we do with Word, I could use the Diff. Alas Microsoft did not build this into the system. A variety of packages that I found on the web did not do such a bright job either. I did not need to see the differences in editing in cells, or whether one cell had a macro and another did not. All I needed was to find which assemblies are common to the two servers and which are unique to each.
Again PowerShell came to the rescue. I love this thing and I enjoy learning new thing about it, so I wrote a little PowerShell script that highlights the differences.
I tested my script using the two sheets below. The asterisks in column E identify the unique entries in each. Entries lacking this asterisk are common. Note that the sheets are also of different lengths.

Finding common and unique rows is an easy enough task. The spreadsheets have to be sorted (both ascending) and compared.
Here is a piece of history. Before personal computers and local area networks, we had central computing. There were Mainframes in the very beginning and then minicomputers. The PC is just about 36 years young. In the very beginning we did not even have dumb terminals by which people could get or feed information into the beast. Instead we used punched tape, punched cards, magnetic tape and disk (much bigger in size, much more expensive and much smaller in capacity). Jobs were run as batches. Still, data processing needed to be done and the sorting and merging of files was a major part of the effort.
I have taken the old IBM mainframe sort-merge algorithm, actually the merge part, and twisted it a little to match the task of comparing files. The compare part is the major ingredient in the merge algorithm, but here I used it for reporting rather than merging.
Enough with history. How is it done? You read the 1st line from both files. If A is less than B, you report A as unique and read A again (and compare again). If B is less than A, you report B as unique and read B. whoever is smaller is reported as unique and its next line is read. If the lines are equal, report them as common and read from both.
I used these two sheets and ran the script. Below are the results. Notice the use of color to accentuate and make it easier to read. The script also produces the same report in rtf format.

I also ran the report on a real set of two GAC lists from two WFE. The screenshot below shows the result of their comparison. The report has hundreds of lines in it so I only showed the end.

Finally the code.
There are two scripts involved. Find them in the following links:
Garnering the GAC assembly list in: http://www.mgsltns.com/GacListToCsv.txt
Comparing the Csvs in: http://www.mgsltns.com/CompareCsv.txt
Before you run them, change the extensions from ‘txt’ to ‘ps1’
Also note that because my site is hosted on a Unix system, the links are case sensitive. You may be better off just clicking on them.
That’s All Folks
Oh, it is best to view code in a smart editor, so change the extension and view the code in Notepad++ or PowerGui Script Editor (or another good editor of your choice)
I have just finished a new CodePlex project named SPDeployRetract. This is a sophisticated PowerShell script that allows you to to deploy solutions and InfoPath forms with ease. It is especially beneficial because once you set up the instructions in the xml input, you can repeat the process in farm after farm without error.
The script also allows for easy retraction. You may always retract the last deployment by setting the instructions to do so. This is possible because every action taken is recorded and old versions of each WSP are backed up for redeployment.
Look it up in: http://spdeployretract.codeplex.com/
That’s all Folks!!
We are in a project and we hit some snags. What’s a snag? An activity that takes longer than expected. Actually it takes longer than the time assigned to it by an over pressed PM who accepts an impossible time table and tries his best to make it possible, but I digress (again!).
So we have snags and we also have the opposite. Let’s call these “cinches”.
The question is: how does a combination of snags and cinches affect the project timeline?
Well, there is no simple answer. It depends on the projects dependencies as we see in the PERT chart. If all the snags are in the critical path and all the cinches are elsewhere then the cinches don’t help at all. In fact any snag in the critical path will delay the project. Conversely, a cinch in the critical path will expedite it. A snag outside the critical path might be serious enough to even change the critical path. Thus without the PERT chart, we cannot really tell.
Still there is a principle involved – Time and speed are non-linear! Twice as long adds a full unit, half as long only takes ½ unit away.
Let’s just investigate a simple project. It consists of two activities – S and C - each estimated to take a week. Alas, S is a snag and really needs twice the time allotted and – a sigh of relief – C is a cinch and will take half the time allotted, so everything is Hun-key-dory, or is it? Even here the PERT chart is important. We have 2 cases:
1: S depends on C (or vice versa) as in when the two activities are assigned to one employee. Here the estimated time was 1 + 1 and the actual time was 2 + ½ and we are ½ week late or 25% late.
2: S and C are done in parallel. Here the estimated time was 1, but the actual time is 2 – we are a whole week or 100% late.
Let’s change the equation a little. S need 1.5 and C needs .5 so in case 1, we have the loss fully compensated by the gain, but in case 2 we are still behind.
There are cases where this really makes no difference. This is when the critical path is not affected and we have enough slack in the other paths to counteract the difference between its snags and cinches – Let’s call this difference DSC. So if the slack is greater than DSC the project will not suffer.
Conclusion: There is no general rule about snags and cinches. We need to examine each case within its project, still as we saw in the 4 examples above; the snag is generally more powerful than the cinch.
Long live Murphy!
That’s All Folks
Well, I have written a sophisticated PowerShell script to expedite the deployment of InfoPath forms - .XSN file. Along the way by way of trial and error (mostly error and error), I discovered a few little things. Here they are.
• Regardless of how the install command is run – PowerShell or the GUI in Central Admin – SharePoint enwraps the XSN inside a solution – WSP, then installs and deploys the solution.
• The solution is named by concatenating “form-“ with the first 16 characters (or less if the file name is shorter than 16) of the file name and the required WSP at the end. So if the form name was MyInfopathForm.xsn the solution name will be form-MyInfopathForm.wsp, but for WithdrawalOfRequestsForRefund.xsn it will be named form-WithdrawalOfRequ.wsp
• It only gets worse! Had there already been a solution file with the same name, Microsoft appends a three digit number to the name, like MyInfopathForm-123.wsp. Remember a digit is a finger, I suspect a middle finger, so when you deploy the same form – many versions of it, or as it was in my case – testing a script time and again, you’ll end up with many such digit (middle finger) appended solutions, all un-deployed except the last one. This is not a bug. It’s a feature!

Well, there are ways around it. When by hand, remove the solution from the solution store before deploying the form again. In the script I do the same thing.
And finally - an important caveat; Make sure that all your form names are unique in the first 16 characters. If you also have a form with the name forWithdrawalOfRequestForRelief.xsn, you’re in trouble!
That’s all folks!
PowerShell Try Catch Finally
I am a relative novice to PowerShell and tried (pun intended) to use the “Try Catch Finally” in my scripts. Alas the structure that we love and use in C# (or even – shudder of shudders - in VB) does not always work in PowerShell. It turns out that it works only when the error is a terminating error (whatever that means).
Well, you can turn all your errors to the terminating kind by simply setting -
$ErrorActionPreference = "Stop", And later resetting it back to “Continue”, which is its normal setting.
Now, the lazy approach is to start all your scripts with:
$ErrorActionPreference = "Stop"
And ending all of them with:
$ErrorActionPreference = "Continue"
But this opens you to trouble because should your script have an error that you neglected to catch (it even happens to me!), your session will now have all its errors as “terminating”. Obviously this is not a good thing, so instead let’s put these two setups in the beginning of each Try block and in the Finally block as seen below:
![clip_image002[6] clip_image002[6]](http://gwb.blob.core.windows.net/pointstoshare/Windows-Live-Writer/8a1ed5891fac_12ED5/clip_image0026_thumb.jpg)
That’s All Folks!!
I have written a nifty little tool in PowerShell that eases the deployment of SharePoint solutions (WSP) files. The tool serves as a management tool for new features. It uses an XML configuration to select a bunch of solutions and deploys them to the farm. Now, despite our best intentions and the best efforts of QA, we somehow manage to upload solutions that do not do exactly what they are supposed to do, or – horror of horrors – even disrupt previous behavior.
So, the tool is also used to restore the site to its previous glory. It is actually a breeze to do so. You just change the purpose to “restore” and point to the backup directory created at the time you “Deploy”ed.
Here is the purpose of this blog: I needed to store the old version of the solution in the backup directory and used the Name property of the Get-SPSolution. WRONG!! the Name property appends a naughty prefix – “SPSolution Name=” to the real name. In the screen shot below, the 1st arrowed line shows this effect and the 2nd shows how it ought to be.


The line surrounded in red shows how the "error" was made, the 2 lines surrounded in blue show how to use the $file.Name in getting the desired result.
That’s All Folks!!
Relative and absolute URLs in CEWP
Microsoft’s own explanation of the CEWP in:http://office.microsoft.com/en-us/sharepoint-server-help/content-editor-web-part-HA010024046.aspx includes the following caveat:
Note: If you enter a URL into the Content Editor Web Part as a relative link, the link converts to an absolute URL when the entry is saved. This automatic conversion can be an issue if you are deploying content from a staging environment to production, where absolute URLs reference the original server's name. To address this automatic conversion issue, you will need to edit the Content Editor Web Part on the production server and update the URLs manually.
Can anything be done about it? How about thinking out of the box? No it’s not the same as OOTB – requiring no coding. It is out of the box that we are usually stuck in by our common education, the blinders that society puts on our eyes. It ought to be named “out of the rut” or OOTR.
So here is the OOTR solution. Put the CEWP HTML in a text file, relative URL included and put it in a document library. Put the link to this text file in the CEWP. Now when you move from one environment to another, as long as the structures of the environments are the same – namely they have the same relative navigation – the conversion will be done without reediting the web parts.
This approach offers an additional advantage. When a specific CEWP is deployed in multiple pages, the HTML is kept in one location. Any future changes will be done ONCE in this central location rather than the painful and error prone approach of editing the CEWP in every page.
The long and the short of is: CEWP content belongs in a text file and kept in a SharePoint library! The CEWP itself will only have the link to this text file, and no HTML of its own!
That’s all folks