May 2, 2009

Power Tag Cloud - SharePoint Tag Cloud written in PowerShell

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.

image

Configure how your cloud should looks like and behaves. The editor panel is written in PowerShell too!

image

The tag cloud

image

When you click on a tag the tag browser shows all items tagged with this tag within the chosen scope.

image

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.

21 comments:

Share said...

Nice article on SharePoint.Thanks!

Gilad Riedl said...

First of all, I love your PowerShell Solutions for SharePoint.
Unfortunately, it seems like the power cloud does not work with (multi-)Lookup fields 'cos SPSiteDataQueries do not support this type of fields.
I wrote a quick workaround (no adjustment for the EditorPart and no performance opt.):
Replace the ViewFields line with the following ...
$query.ViewFields = "<FieldRef Name='$tagField' Nullable='True' />"

and replace the line starting with "$dataTable | Select-Object Tags (...)" with
$dataTable.Rows | foreach {
$list = $web.Lists[[System.Guid]$_.ListId]
$item = $list.GetItemById($_.ID)
$item["Category"] | %{$tags.Add($_.LookupValue)}
}

Greetings

harbyherbman said...
This comment has been removed by a blog administrator.
Julia Patrick said...

Including a sharepoint digital signature makes things easier for the users. Hope they won't experience further errors.

Hermes iPhone 6 plus cases said...

If reports are true, the iPad Mini 4 will feature A8X processor and an ultra thin iPad Air 2-like design. The release date of the next-generation iPad Air 2 and iPad Mini 4 is expected to be "late 2015."

maymay said...

Your current enjoy need to search brand-new in any respect rolex replica. Whenever they are getting to be previous marketing and advertising to these people, since and then merely you can search in a big way very good. Designer watches are certainly not actually to get high priced nevertheless must be in a very very good along with able to be used issue. There isn't a go with involving brand name designer watches, in case obtaining income is equipped with these people. They're not going to rolex replica elevate your current type nevertheless will certainly share very same for a visual appeal. Does one are living in england? If you do, it is likely you recognize how bothersome it is usually to never get comprehensive entry to Netflix. Nearly all BRITISH ISLES people would die to have your National variation involving Netflix. That they would die to enjoy a common internet streaming videos along with tv shows through the convenience with their household. Nevertheless on account of standards these are rolex replica sale to accomplish this and quite often speculate tips on how to enjoy Netflix US throughout BRITISH ISLES. Immediately after undertaking a number of straightforward analysis pertaining to Netflix BRITISH ISLES VPN on-line maybe you have discover a number of straightforward possibilities. On the other hand, in many cases these kind of possibilities steady stream little by little as well as are generally not capable of providing you entire entry to Netflix. Various other periods your choices on hand are generally way too intricate to be aware of or perhaps charge a significant amount of income. breitling replica uk, most of the people throw in the towel along with leave seeking replies about tips on how to enjoy Netflix US throughout BRITISH ISLES. One thing you'll want to ascertain can be which unit that you've what is the best you would like to gain access to Netflix BRITISH ISLES VPN. This will likely rolex replica uk which in turn possibilities are generally on hand to start with seeing written content in Netflix.

Hug Day said...

hug day 2016 Images
happy hug day 2016 Date SMS Messages Quotes
Hug day Celebration Ideas For Him/Her
hug day sms for gf

风骚达哥 said...

20160423 junda
air jordans
cheap oakley sunglasses
kate spade outlet
adidas stan smith
ray ban sunglasses
michael kors outlet
nike air max shoes
cheap omega watches
yeezy boost 350
michael kors outlet online
michael kors outlet online
nike free runs
air jordan uk
michael kors outlet online
pandora charms
ray ban outlet
calvin klein outlet
fitflops sale clearance
toms outlet
nike huarache white
sac longchamp
toms outlet
michael kors outlet clearance
bottega veneta outlet
armani watches
gucci handbags
nike blazer
oakley sunglasses
true religion jeans
cheap jordans
ray bans
hollister
bottega veneta handbags
jimmy choo outlet
oakley sunglasses
converse
prada outlet
reebok shoes
true religion
burberry outlet

Libin Huang said...

20160426libinferragamo outlet
pandora outlet
ferragamo shoes
nike air max
ferragamo shoes
hermes belt for sale
michael kors factory outlet
swarovski crystal
ferragamo shoes
michael kors outlet
louis vuitton handbags
louis vuitton handbags outlet
swarovski outlet
beats by dre
cazal outlet
mont blanc pens
longchamp solde
fitflops outlet
cartier sunglasses for men
michael kors outlet
louis vuitton outlet
swarovski crystal
cheap nfl jerseys
adidas outlet store
tory burch outlet
cheap oakley sunglasses
mulberry bags
rolex watches,rolex watches,swiss watches,watches for men,watches for women,omega watches,replica watches,rolex watches for sale,rolex replica,rolex watch,cartier watches,rolex submariner,fake rolex,rolex replica watches,replica rolex
oakley sunglasses
tory burch outlet
ray ban sunglasses
louis vuitton outlet
polo ralph lauren
michael kors uk
tiffany outlet

Fangyaya said...

michael kors handbags
coach factory outlet online
ghd flat iron
louis vuitton handbags
ralph lauren
polo shirts
ray ban sonnenbrille
nfl jerseys wholesale
kate spade outlet
ray ban sunglasses
coach outlet online
coach outlet online
louis vuitton outlet stores
christian louboutin sale
giuseppe zanotti sneakers
cheap ray ban sunglasses
michael kors outlet clearance
louboutin shoes
air jordan shoes
tods sale
replica rolex watches
coach outlet
coach outlet store online
louis vuitton outlet stores
coach outlet store online clearances
adidas yeezy
tory burch flats
polo ralph lauren
coach outlet store online clearances
air jordan 11
20168.8chenjinyan

Justin said...

Elected understudy credits are given by the legislature, and can be paid specifically to the school, the understudy, or the guardians. Elected credits might be sponsored by the administration, or unsubsidized relying upon the money related need of the understudy. cash advance chicago

Justin said...

These advances normally accompany strict terms and conditions and must be utilized to pay for instruction costs at the school that has affirmed your participation. Training costs can include educational cost, food and lodging, books, school charges, transportation, gear, and ward youngster care costs. https://www.usacheckcashingstore.com/san-diego

marko said...

Ordinarily, the administration additionally has various offers for the understudies which can't generally meet the requests of the understudies. That is the reason the understudies need outside money related help which are guided by the private credits from various monetary establishments. https://www.usapaydayloanstore.com/chicago

Paulo said...

Along these lines, the PLUS understudy credit goes a tiny bit further.College instruction is definitely not shabby, and as the advances taken out to cover educational cost charges and everyday costs develop, understudies in the end confront a tremendous obligation. https://www.usapaydayloanstore.com/chicago

Paulo said...

A few moneylenders don't offer the advances they begin, and it is conceivable the same bank may be as yet holding and adjusting the advance. Such banks have more control to give a credit alteration. https://www.usacheckcashingstore.com/san-diego

John said...

air force 1
birkenstock outlet
ugg sale
fitflops sale
michael kors outlet store
canada goose jackets
nike uk
converse all star
ralph lauren outlet online
coach outlet online
birkenstock outlet
abercrombie
polo ralph lauren outlet
adidas nmd r1
mcm outlet online
coach outlet online
timberland boots outlet
ed hardy store
true religion jeans outlet
nike cortez classic
timberland shoes
louis vuitton outlet online
birkenstock sale
louboutin outlet
michael kors handbags outlet
uggs sale
birkin handbags
rolex watches for sale
michael kors outlet store
vans outlet
polo ralph lauren outlet online
20161102yuanyuan

Unknown said...

cartier outlet
fitflops sale clearance
sac louis vuitton pas cher
longchamp outlet online
mulberry uk
thomas sabo charms
cheap oakley sunglasses
cheap nike shoes
ray-ban sunglasses
ralph lauren
chanyuan2017.05.08

raybanoutlet001 said...

true religion outlet
michael kors purses
michael kors outlet
versace jeans
yeezy boost 350 white
saics running shoes
oakley sunglasses
reebok shoes
nike tn
fitflops

Home Improvement NYC said...

Ajrin Construction Inc. is a licensed New York general construction firm working as General Contractors in NYC, truly devoted to our services and remain firm until our Clients’ purposes are served to satisfaction. We feel much concerned of the value of your property, safety and security more than our profit or anything else.Home Improvement NYC

Teresa Hamilton said...

Thank you for sharing the article. It's what I need to know.
instagram viewer

اميرة زماني said...


شركة كشف تسربات المياه بالاحساء
شركة كشف تسربات المياه بالدمام
شركة كشف تسربات المياه بالجبيل
شركة جلى بلاط شرق الرياض
افضل شركة تسليك مجاري بالاحساء
اسعار شركات تنظيف منازل بالرياض