I'm a tag cloud fan but unfortunately there isn’t any in SharePoint 2007 out of the box. So let’s write a reusable tag cloud as an example for PowerWebPart. The Power Tag Cloud consists of two web parts, the tag cloud itself and the tag browser.
Tag an item. You can either use a text field with a semicolon as separator or any lookup field. Maybe I show in following post how you can pimp the tag field with a jQuery AJAX auto completion script and PowerWebPart.
Configure how your cloud should looks like and behaves. The editor panel is written in PowerShell too!
The tag cloud
When you click on a tag the tag browser shows all items tagged with this tag within the chosen scope.
All this stuff is written in PowerShell!
To get a feeling how PowerWebPart scripts look like:
Power Tag Cloud Script
#init parameters
$scope = Init-Parameter "Parameter1" [String]::Empty
$tagField = Init-Parameter "Parameter2" "Tags" -defaultOnEmpty
$maxFontSize = Init-Parameter "Parameter3" "40" -defaultOnEmpty
$minFontSize = Init-Parameter "Parameter4" "6" -defaultOnEmpty
$color1 = Init-Parameter "Parameter5" "#003399" -defaultOnEmpty
$color2 = Init-Parameter "Parameter6" "#CCCCCC" -defaultOnEmpty
$listType = Init-Parameter "Parameter7" "0" -defaultOnEmpty
$tagBrowser = Init-Parameter "Parameter8" [String]::Empty
# calculate gradient
$r1 = [Convert]::ToInt32($color1.Substring(1,2),16)
$g1 = [Convert]::ToInt32($color1.Substring(3,2),16)
$b1 = [Convert]::ToInt32($color1.Substring(5,2),16)
$r2 = [Convert]::ToInt32($color2.Substring(1,2),16)
$g2 = [Convert]::ToInt32($color2.Substring(3,2),16)
$b2 = [Convert]::ToInt32($color2.Substring(5,2),16)
$diff_r = $r2-$r1
$diff_g = $g2-$g1
$diff_b = $b2-$b1
# css
$defaultCss =@"
.power-cloud A {
TEXT-DECORATION: none
}
.power-cloud A:hover {
TEXT-DECORATION: underline
}
.power-cloud UL {
PADDING-RIGHT: 0px;
PADDING-LEFT: 0px;
PADDING-BOTTOM: 0px;
MARGIN: 0px;
PADDING-TOP: 0px;
LIST-STYLE-TYPE: none;
TEXT-ALIGN: center
}
.power-cloud LI
{
PADDING-RIGHT: 0px;
DISPLAY: inline;
PADDING-LEFT: 0px;
BACKGROUND-IMAGE: none !important;
PADDING-BOTTOM: 0px;
MARGIN: 0px;
PADDING-TOP: 0px;
TEXT-ALIGN: justify
}
"@
$css = Init-Parameter "Parameter9" $defaultCss -defaultOnEmpty
Register-CSSBlock $css
function CreateChildControls($controls)
{
#find all tagged list items
$query = New-Object "Microsoft.SharePoint.SPSiteDataQuery"
$query.Query = @"
"@
$query.ViewFields = ""
#set query scope
if([String]::IsNullOrEmpty($scope) -eq $false){ $query.Webs=""}
#set list type
$query.Lists = ""
$dataTable = $web.GetSiteData($query)
# extract all tags in a list
$global:tags = New-Object System.Collections.ArrayList
$dataTable | Select-Object Tags | %{$_.Tags.Split(';')} | %{$tags.Add($_.Trim().TrimStart('#'))}
# group tags
$global:tagGroups = $tags | Group-Object | Sort-Object Count -Descending
}
function Render($writer)
{
$gradientSteps = $tagGroups.Count
#begin cloud
$writer.Write("")
for($i=0; $i -lt $tagGroups.Count; $i++ )
{
# calculate font size
$size = [Math]::Round(($tagGroups[$i].Count * $maxFontSize) / $tagGroups[0].Count)
if($size -lt $minFontSize){$size = $minFontSize}
# calculate color
$gradientFactor = $i / $gradientSteps
$r = $r1 + $diff_r * $gradientFactor
$g = $g1 + $diff_g * $gradientFactor
$b = $b1 + $diff_b * $gradientFactor
$color = "rgb($r, $g, $b)"
$tagName = $tagGroups[$i].Name
$tagUrl = "{0}?tag={1}&listType={2}&scope={3}&tagField={4}" -f $tagBrowser, $tagName, $listType ,$scope, $tagField
#render tag
$writer.Write("- `n$tagName`n")
}
# end cloud
$writer.Write("
")
}
Power Tag Cloud Editor Script $txtMaxFontSize = New-Object System.Web.UI.WebControls.TextBox
$txtMaxFontSize = New-Object System.Web.UI.WebControls.TextBox
$txtMinFontSize = New-Object System.Web.UI.WebControls.TextBox
$txtMaxColor = New-Object System.Web.UI.WebControls.TextBox
$txtMinColor = New-Object System.Web.UI.WebControls.TextBox
$txtTagField = New-Object System.Web.UI.WebControls.TextBox
$txtTagBrowser = New-Object System.Web.UI.WebControls.TextBox
$drpdScope = New-Object System.Web.UI.WebControls.DropDownList
$drpdLists = New-Object System.Web.UI.WebControls.DropDownList
$txtCss = New-Object iLoveSharePoint.WebControls.SimpleTextEditor
function CreateChildControls($controls)
{
$drpdScope.Items.Add([String]::Empty)
$drpdScope.Items.Add("Recursive")
$drpdScope.Items.Add("SiteCollection")
$drpdLists.Items.Add($(New-Object System.Web.UI.WebControls.ListItem("Generic list","0")))
$drpdLists.Items.Add($(New-Object System.Web.UI.WebControls.ListItem("Document Library","1")))
$drpdLists.Items.Add($(New-Object System.Web.UI.WebControls.ListItem("Discussion forum","3")))
$drpdLists.Items.Add($(New-Object System.Web.UI.WebControls.ListItem("Vote or Survey","4")))
$drpdLists.Items.Add($(New-Object System.Web.UI.WebControls.ListItem("Issues List","5")))
$controls.Add($drpdScope)
$controls.Add($drpdLists)
$controls.Add($txtTagField)
$controls.Add($txtMaxFontSize)
$controls.Add($txtMinFontSize)
$controls.Add($txtMaxColor)
$controls.Add($txtMinColor)
$controls.Add($txtTagBrowser)
$txtCss.DisplayText = "Edit CSS"
$controls.Add($txtCss)
}
function OnSyncChanges()
{
$this.EnsureChildControls()
$drpdScope.SelectedValue = $webpart.Parameter1
$txtTagField.Text = $webpart.Parameter2
$txtMaxFontSize.Text = $webpart.Parameter3
$txtMinFontSize.Text = $webpart.Parameter4
$txtMaxColor.Text = $webpart.Parameter5
$txtMinColor.Text = $webpart.Parameter6
$drpdLists.SelectedValue = $webpart.Parameter7
$txtTagBrowser.Text = $webpart.Parameter8
$txtCss.Text = $webpart.Parameter9
}
function OnApplyChanges()
{
$this.EnsureChildControls()
$webpart.Parameter1 = $drpdScope.SelectedValue
$webpart.Parameter2 = $txtTagField.Text
$webpart.Parameter3 = $txtMaxFontSize.Text
$webpart.Parameter4 = $txtMinFontSize.Text
$webpart.Parameter5 = $txtMaxColor.Text
$webpart.Parameter6 = $txtMinColor.Text
$webpart.Parameter7 = $drpdLists.SelectedValue
$webpart.Parameter8 = $txtTagBrowser.Text
$webpart.Parameter9 = $txtCss.Text
return $true
}
function Render($writer)
{
$writer.Write("
")
Render-Row "Scope" $drpdScope $writer
Render-Row "List Type" $drpdLists $writer
Render-Row "Tag Column Name" $txtTagField $writer
Render-Row "Tag Browser Url" $txtTagBrowser $writer
Render-Row "Max font size" $txtMaxFontSize $writer
Render-Row "Min font size" $txtMinFontSize $writer
Render-Row "Max color (hex)" $txtMaxColor $writer
Render-Row "Min color (hex)" $txtMinColor $writer
Render-Row "Style" $txtCss $writer
$writer.Write("
")
}
function Render-Row($text, $control, $writer)
{
$writer.Write("
")
$writer.Write("") $writer.Write("$text ") $control.RenderControl($writer) $writer.Write(" | ")
$writer.Write("
")
}
Power Tag Cloud Browser Script function CreateChildControls($controls)
{
$global:tag = $page.Request["tag"].ToString()
$global:scope = $page.Request["scope"]
$global:listType = $page.Request["listType"].ToString()
$global:tagField = $page.Request["tagField"].ToString()
$query = New-Object "Microsoft.SharePoint.SPSiteDataQuery"
#set query scope
if([String]::IsNullOrEmpty($scope) -eq $false){ $query.Webs=""}
$query.Lists = ""
$query.Query = @"
$tag
"@
$query.ViewFields = ""
if($listType -ne "1"){$query.ViewFields +=""}
$global:table = $web.GetSiteData($query)
}
function Render($writer)
{
$writer.Write("Tag: $tag
")
foreach($row in $table)
{
$writer.Write("")
$writer.Write($(Build-Headline $row))
$writer.Write($(Build-Body $row))
$writer.Write("
")
}
}
function Build-Headline($row)
{
$siteId = $site.ID
$href = "/_layouts/CopyUtil.aspx?Use=id&Action=dispform&ItemId={0}&ListId={1}&WebId={2}&SiteId=$siteId" -f $row["Id"],$row["ListId"],$row["WebId"]
if($global:table.Columns.Contains('Title') -eq $false -or [String]::IsNullOrEmpty($row["Title"]))
{
$title = $row["FileLeafRef"].Split('#')[1]
}
else
{
$title = $row["Title"]
}
$html = "" -f $href, $title
return $html
}
function Build-Body($row)
{
$tags = $row[$tagField].Split(';')
$html = "Tags: "
foreach($tag in $tags)
{
$tag = $tag.Trim().TrimStart('#')
$html += "$tag "
}
$html += "
"
return $html
}
function OnError($ex, $writer)
{
$writer.Write("Please ensure that tag, listType, tagField and scope url parameters are provided.")
}
You can download the two wepart files on the PowerWebPart 3.0 release homepage and then import them into the web part gallery or just copy and paste the scripts. An exported PowerWebPart .webart file self contains it script and digital signature, so you can simply export and import PowerWebParts. An end user will never notice if the web part is written in PowerShell or C#. Only farm admins can see and change the scripts. Note that when you import a PowerWebPart from a foreign SharePoint farm the signature will be invalid. A farm admin can simply resign the script through a click on “Apply” in the web part’s editor panel.