Skip to content

Instantly share code, notes, and snippets.

@thedavecarroll
Last active December 22, 2019 18:49
Show Gist options
  • Save thedavecarroll/5273424745bf4dfc57c08b62bd53a2b5 to your computer and use it in GitHub Desktop.
Save thedavecarroll/5273424745bf4dfc57c08b62bd53a2b5 to your computer and use it in GitHub Desktop.
IronScripter Challenge - December 17, 2019 - A PowerShell Challenge for Challenges
function Get-WPSite {
[CmdLetBinding()]
param(
[Parameter(Mandatory)]
[uri]$Url
)
if ($Url.AbsoluteUri -notmatch 'wp-json') {
[uri]$Url = $Url.AbsoluteUri,'wp-json' -join '/'
}
try {
Invoke-RestMethod -Uri $Url.AbsoluteUri.ToString() -ErrorAction Stop -Verbose:$false
}
catch {
$PSCmdlet.ThrowTerminatingError($_)
}
}
function Get-WPSiteLink {
[CmdLetBinding()]
param(
[Parameter(Mandatory)]
[uri]$Url,
[ValidateSet('Categories','Posts','Tags','Users','All')]
[string]$Element='All'
)
if ($Url.AbsoluteUri -notmatch 'wp-json') {
[uri]$Url = $Url.AbsoluteUri,'wp-json' -join '/'
}
try {
$WPSiteLinks = (Invoke-RestMethod -Uri $Url.AbsoluteUri.ToString() -ErrorAction Stop -Verbose:$false).routes.psobject.Properties |
Select-Object @{l='Route';e={$_.Name}} -ExpandProperty value
}
catch {
$PSCmdlet.ThrowTerminatingError($_)
}
if ($Element -eq 'All') {
$WPSiteLinks
} else {
$SearchString = '{0}$' -f $Element.ToLower()
$WPSiteLinks.where({ $_._links.self -match $SearchString })._links.self
}
}
function Get-WPElement {
[CmdLetBinding()]
param(
[Parameter(Mandatory,ValueFromPipeline)]
[uri]$ElementUrl,
[int]$MaxPages = 10
)
for ($PageCount = 1; $PageCount -le $MaxPages; $PageCount++) {
$Uri = '{0}/?page={1}' -f $ElementUrl,$PageCount
try {
$WPElement = Invoke-RestMethod -Uri $Uri -ErrorAction SilentlyContinue -Verbose:$false
if ($WPElement.count -gt 0) {
'Retrieved {0}' -f $Uri | Write-Verbose
$WPElement.PsObject.BaseObject
} else {
break
}
}
catch {
break
}
}
}
function Get-WPPost {
[CmdLetBinding()]
param(
[Parameter(Mandatory)]
[uri]$Url,
[int]$Last
)
try {
$SiteLinks = Get-WPSiteLink -Url $Url -ErrorAction Stop
$TagsUrl = $SiteLinks.where({$_._links.self -match 'tags$'})._links.Self
$Tags = Get-WPElement -ElementUrl $TagsUrl -ErrorAction Stop
$CategoriesUrl = $SiteLinks.where({$_._links.self -match 'categories$'})._links.Self
$Categories = Get-WPElement -ElementUrl $CategoriesUrl -ErrorAction Stop
$UsersUrl = $SiteLinks.where({$_._links.self -match 'users$'})._links.Self
$Users = Get-WPElement -ElementUrl $UsersUrl -ErrorAction Stop
$PostsUrl = $SiteLinks.where({$_._links.self -match 'posts$'})._links.Self
$WPPost = Get-WPElement -ElementUrl $PostsUrl -ErrorAction Stop
}
catch {
$PSCmdlet.ThrowTerminatingError($_)
}
$SelectParam = @{}
if ($Last) {
$SelectParam.Add('Last',$Last)
}
$WPPost |
Sort-Object -Property date |
Select-Object @SelectParam @{l='DateCreated';e={$_.date}},
@{l='DateModified';e={$_.modified}},
@{l='Title';e={$_.title.rendered}},
@{l='Link';e={$_.link}},
@{l='Author';e={
$author = $_.Author;
$Users.Where({$_.id -eq $author}).Name
}},
@{l='Status';e={$_.status}},
@{l='Slug';e={$_.slug}},
@{l='Categories';e={
$JoinCategories = foreach ($cat in $_.categories) {
$Categories.Where({$_.id -eq $cat}).Name
}
$JoinCategories -join ', '
}},
@{l='Tags';e={
$JoinTags = foreach ($tag in $_.tags) {
$Tags.Where({$_.id -eq $tag}).Name
}
$JoinTags -join ', '
}},
@{l='Excerpt';e={([System.Net.WebUtility]::HtmlDecode($_.excerpt.rendered) -replace '<[^>]+>','' ).trim()}}
}
[CmdLetBinding()]
param(
[Parameter(Mandatory)]
[string]$FilePath
)
try {
Import-Module .\2019-12-17_Challenge-for-Challenges.psm1 -ErrorAction Stop -Force -Verbose:$false
'Getting SiteInfo' | Write-Verbose
$SiteInfo = Get-WPSite -Url 'https://ironscripter.us' -ErrorAction Stop
'Getting Posts' | Write-Verbose
$Posts = Get-WPPost -Url 'https://ironscripter.us' -ErrorAction Stop | Where-Object {$_.Categories -match 'Challenge'}
}
catch {
$PSCmdlet.ThrowTerminatingError($_)
}
if ($Posts) {
'Building HTML' | Write-Verbose
$StringBuilder = [System.Text.StringBuilder]::new()
[void]$StringBuilder.Append('<h2>{0}</h2>' -f $SiteInfo.Description)
[void]$StringBuilder.Append('<p>Challenge Posts</p>')
$Style = @'
<style>
body { background-color:#FFFFFF;
font-family:sans-serif;
font-size:10pt; }
td, th { border:0px solid black;
border-collapse:collapse;}
th { color:white;
background-color:black; }
table, tr, td, th { padding: 2px; margin: 0px ; }
tr:nth-child(odd) {background-color: lightgray}
table { width:95%;margin-left:5px; margin-bottom:20px;}
h2 {
font-family:Tahoma;
color:#6D7B8D;
}
.alert {
color: red;
}
.footer
{ color:green;
margin-left:10px;
font-family:Tahoma;
font-size:8pt;
font-style:italic;
}
</style>
'@
$ConvertParams = @{
Head = $Style
Title = '{0} Challenges' -f $SiteInfo.Name
}
[xml]$Content = $Posts |
Select-Object DateCreated,DateModified,@{l='Title';e={'<a href="{0}">{1}</a>' -f $_.Link,$_.Title }},Author,Categories,Tags,Excerpt |
ConvertTo-Html -Fragment
# add css class to table
$TableClass = $Content.CreateAttribute('class')
$TableClass.Value = 'pure-table pure-table-striped'
[void]$Content.table.Attributes.Append($TableClass)
for ($i=1;$i -le $Content.table.tr.count -1 ;$i++ ) {
foreach ($Column in 0,1,2,3) {
[void]$Content.table.tr[$i].ChildNodes[$Column].SetAttribute('nowrap','nowrap')
}
}
# remove unused colgroup
$Column = $Content.GetElementsByTagName('colgroup')[0]
[void]$Content.DocumentElement.RemoveChild($Column)
# add thead to table first row (header)
$HeaderRow = $Content.GetElementsByTagName('tr')[0]
$THead = $Content.CreateDocumentFragment()
$HeaderXml = '<thead>{0}</thead>' -f $HeaderRow.OuterXml
$THead.InnerXml = $HeaderXml
[void]$Content.FirstChild.RemoveChild($HeaderRow)
[void]$Content.table.PrependChild($THead)
# add tbody to table
$DataXml = foreach ($Row in $Content.SelectNodes('/table/tr')) {
if ($Row.ParentNode.Name -ne 'thead') {
$Row.OuterXml
[void]$Content.table.RemoveChild($Row)
}
}
$TBody = $Content.CreateDocumentFragment()
$TBody.InnerXml = '<tbody>{0}</tbody>' -f ($DataXml -join "`n")
[void]$Content.table.AppendChild($TBody)
# convert Xml and Html encoded characters to nice text
$Body = $Content.InnerXml | Out-String | Foreach-Object { [System.Net.WebUtility]::HtmlDecode($_)}
# beautify body Html
$BodyHtml = [System.Xml.Linq.XElement]::Parse($Body).ToString()
[void]$StringBuilder.Append($BodyHtml)
[void]$StringBuilder.Append('<p class="footer">There are {0} posts with a category of Challenge.</p>' -f $Posts.Count)
$ConvertParams.Add('Body',$StringBuilder.ToString())
'Saving report to {0}' -f $FilePath | Write-Verbose
ConvertTo-Html @ConvertParams | Out-File -FilePath $FilePath
}
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<style>
body { background-color:#FFFFFF;
font-family:sans-serif;
font-size:10pt; }
td, th { border:0px solid black;
border-collapse:collapse;}
th { color:white;
background-color:black; }
table, tr, td, th { padding: 2px; margin: 0px ; }
tr:nth-child(odd) {background-color: lightgray}
table { width:95%;margin-left:5px; margin-bottom:20px;}
h2 {
font-family:Tahoma;
color:#6D7B8D;
}
.alert {
color: red;
}
.footer
{ color:green;
margin-left:10px;
font-family:Tahoma;
font-size:8pt;
font-style:italic;
}
</style>
</head><body>
<h2>Whose Code Will Reign Supreme?</h2><p>Challenge Posts</p><table class="pure-table pure-table-striped">
<thead>
<tr>
<th>DateCreated</th>
<th>DateModified</th>
<th>Title</th>
<th>Author</th>
<th>Categories</th>
<th>Tags</th>
<th>Excerpt</th>
</tr>
</thead>
<tbody>
<tr>
<td nowrap="nowrap">4/26/2019 3:16:29 PM</td>
<td nowrap="nowrap">5/9/2019 2:42:12 PM</td>
<td nowrap="nowrap">
<a href="https://ironscripter.us/a-challenge-from-the-dark-faction/">A Challenge from the Dark Faction</a>
</td>
<td nowrap="nowrap">Jeffery Hicks</td>
<td>Challenge, Iron Scripter</td>
<td>DarkFaction, IronScripter2019</td>
<td>The annual PowerShell Summit is fast approaching and at this late hour a challenge has been delivered from the Dark Faction to those who feel they are worthy of the title “Iron Scripter”. They doubt your skill and resolve. The Dark Faction scripts in the shadows and has always felt they are the true champions. …</td>
</tr>
<tr>
<td nowrap="nowrap">4/28/2019 10:56:17 PM</td>
<td nowrap="nowrap">5/9/2019 2:42:03 PM</td>
<td nowrap="nowrap">
<a href="https://ironscripter.us/are-you-worthy-of-the-dark-faction/">Are You Worthy of the Dark Faction?</a>
</td>
<td nowrap="nowrap">Jeffery Hicks</td>
<td>Challenge, Iron Scripter</td>
<td>challenge, DarkFaction</td>
<td>Recently the Chairman posted a challenge from the Dark Faction. However the Dark Faction is not satisfied. They have delivered another challenge for those of you who wish to prove your worthiness as an Iron Scripter. The Dark Faction often shares code among its members. You are challenged to develop a set of tools that …</td>
</tr>
<tr>
<td nowrap="nowrap">5/9/2019 1:12:25 PM</td>
<td nowrap="nowrap">5/9/2019 1:12:26 PM</td>
<td nowrap="nowrap">
<a href="https://ironscripter.us/solving-the-dark-factions-powershell-challenge/">Solving the Dark Faction’s PowerShell Challenge</a>
</td>
<td nowrap="nowrap">Jeffery Hicks</td>
<td>Challenge, Iron Scripter</td>
<td>challenge, DarkFaction</td>
<td>Not too long ago the Dark Faction posted a challenge for those of you who felt worthy to call yourself an Iron Scripter.  Now that this year’s Iron Scripter battle is complete, the Chairman has arranged for a solution to the Dark Faction’s challenge. You could have written a single script or broken the task …</td>
</tr>
<tr>
<td nowrap="nowrap">5/10/2019 4:30:53 PM</td>
<td nowrap="nowrap">5/10/2019 4:30:53 PM</td>
<td nowrap="nowrap">
<a href="https://ironscripter.us/solving-the-dark-factions-powershell-transport-challenge/">Solving the Dark Faction’s PowerShell Transport Challenge</a>
</td>
<td nowrap="nowrap">Jeffery Hicks</td>
<td>Challenge</td>
<td>advanced, challenge, DarkFaction</td>
<td>Before the most recent PowerShell + DevOps Global Summit, the Dark Faction issued a second challenge. Certainly, working code that produces the desired results is important. But equally important is how you might approach this problem. What tools or techniques might you consider? How you work is as important to the Dark Faction as your …</td>
</tr>
<tr>
<td nowrap="nowrap">6/7/2019 12:23:50 PM</td>
<td nowrap="nowrap">12/17/2019 5:40:06 PM</td>
<td nowrap="nowrap">
<a href="https://ironscripter.us/let-the-powershell-challenges-begin/">Let the PowerShell Challenges Begin</a>
</td>
<td nowrap="nowrap">Jeffery Hicks</td>
<td>Challenge</td>
<td>challenge, intermediate</td>
<td>The Chairman has decided that it is in the best interests of his Iron Scripters, and those that wish to attain that valued designation, that training continue year-round. To that end, he has commissioned a series of PowerShell challenges. These challenges will range in complexity and be tagged accordingly. Although, you are encouraged to try …</td>
</tr>
<tr>
<td nowrap="nowrap">6/28/2019 2:55:39 PM</td>
<td nowrap="nowrap">12/2/2019 4:03:28 PM</td>
<td nowrap="nowrap">
<a href="https://ironscripter.us/a-powershell-cryptogram/">A PowerShell Cryptogram</a>
</td>
<td nowrap="nowrap">Jeffery Hicks</td>
<td>Challenge</td>
<td>advanced, challenge</td>
<td>The Chairman is back with another challenge, this one probably more on the harder end. Although once you get your head around how to solve it, you’ll probably find it pretty easy. Your challenge is to solve a cryptogram like this, presumably using PowerShell. Inside this string block is a hidden message. To solve or …</td>
</tr>
<tr>
<td nowrap="nowrap">7/15/2019 7:56:38 PM</td>
<td nowrap="nowrap">7/19/2019 8:15:41 PM</td>
<td nowrap="nowrap">
<a href="https://ironscripter.us/powershell-beginners-have-to-start-somewhere/">PowerShell Beginners Have to Start Somewhere</a>
</td>
<td nowrap="nowrap">Jeffery Hicks</td>
<td>Challenge</td>
<td>beginner, challenge</td>
<td>As promised, the Chairman is offering up the following challenge for PowerShell beginners. Get all files in a given folder including subfolders and display a result that shows the total number of files, the total size of all files, the average file size, the computer name, and the date when you ran the command. This …</td>
</tr>
<tr>
<td nowrap="nowrap">7/19/2019 8:13:10 PM</td>
<td nowrap="nowrap">7/19/2019 8:15:24 PM</td>
<td nowrap="nowrap">
<a href="https://ironscripter.us/a-powershell-scripting-challenge-for-everyone/">A PowerShell Scripting Challenge for Everyone</a>
</td>
<td nowrap="nowrap">Jeffery Hicks</td>
<td>Challenge</td>
<td>advanced, beginner, challenge, intermediate</td>
<td>Hopefully, everyone has had an opportunity to work on the previously posted beginner challenge. For those of you with a bit more experience, it was probably an easy task. Now that you have a solution that you can run at a PowerShell prompt, your next challenge will build from it. The basic challenge is to …</td>
</tr>
<tr>
<td nowrap="nowrap">7/31/2019 2:06:09 PM</td>
<td nowrap="nowrap">12/17/2019 5:43:06 PM</td>
<td nowrap="nowrap">
<a href="https://ironscripter.us/building-more-powershell/">Building More PowerShell</a>
</td>
<td nowrap="nowrap">Jeffery Hicks</td>
<td>Challenge</td>
<td>advanced, beginner, challenge, intermediate</td>
<td>The Chairman hopes that people have been working on the recent set of challenges. They were designed to meet the needs of all skill levels. The Chairman does not want IT Pros and PowerShell fans to think these challenges are only for advanced and experienced people. You can’t become advanced and experienced without using PowerShell. …</td>
</tr>
<tr>
<td nowrap="nowrap">8/19/2019 8:26:37 PM</td>
<td nowrap="nowrap">12/2/2019 4:02:14 PM</td>
<td nowrap="nowrap">
<a href="https://ironscripter.us/a-windows-feature-powershell-challenge/">A Windows Feature PowerShell Challenge</a>
</td>
<td nowrap="nowrap">Jeffery Hicks</td>
<td>Challenge</td>
<td>advanced, beginner, challenge, intermediate</td>
<td>It is time for a new set of challenges. Please feel free to post a comment with links back to your solutions and work on previous challenges. The Chairman doesn’t want to get into a position of having to provide solutions because there’s no guarantee it would be any “better” than yours. If your code …</td>
</tr>
<tr>
<td nowrap="nowrap">10/8/2019 6:04:54 PM</td>
<td nowrap="nowrap">12/2/2019 4:03:17 PM</td>
<td nowrap="nowrap">
<a href="https://ironscripter.us/a-powershell-cross-platform-challenge/">A PowerShell Cross-Platform Challenge</a>
</td>
<td nowrap="nowrap">Jeffery Hicks</td>
<td>Challenge</td>
<td>advanced, challenge</td>
<td>As PowerShell 7 draws near, built on the successes of PowerShell Core, you will be asked to create PowerShell tools that work cross-platform. Or you may be asked to create a PowerShell tool to run on a non-Windows platform. Your command should still be centered on writing objects to the pipeline. Even if the source …</td>
</tr>
<tr>
<td nowrap="nowrap">10/30/2019 1:38:31 PM</td>
<td nowrap="nowrap">12/2/2019 4:03:04 PM</td>
<td nowrap="nowrap">
<a href="https://ironscripter.us/raise-the-dead-with-this-powershell-challenge/">Raise the Dead with this PowerShell Challenge</a>
</td>
<td nowrap="nowrap">Jeffery Hicks</td>
<td>Challenge</td>
<td>advanced, challenge</td>
<td>Halloween is upon us and the Chairman has devised a demonic PowerShell scripting challenge for you. This is not for the faint of heart. The Chairman wants you to raise the dead. Specifically, deleted items in your Recycle Bin on a Windows 10 desktop. There are several parts to this challenge. First, calculate how much …</td>
</tr>
<tr>
<td nowrap="nowrap">11/15/2019 3:29:27 PM</td>
<td nowrap="nowrap">12/2/2019 4:02:53 PM</td>
<td nowrap="nowrap">
<a href="https://ironscripter.us/a-beginner-powershell-function-challenge/">A Beginner PowerShell Function Challenge</a>
</td>
<td nowrap="nowrap">Jeffery Hicks</td>
<td>Challenge</td>
<td>beginner, challenge</td>
<td>The Chairman realizes that many IT Pros who take on his challenges are experienced and advanced PowerShell users. But everyone was a beginner once. Everyone had that first challenge of writing a PowerShell function. Today’s challenge is for those of you in the early stages of learning PowerShell. Now, it is time for you to …</td>
</tr>
<tr>
<td nowrap="nowrap">11/27/2019 2:17:57 PM</td>
<td nowrap="nowrap">12/2/2019 4:02:37 PM</td>
<td nowrap="nowrap">
<a href="https://ironscripter.us/are-you-listening-to-me/">Are You Listening to Me</a>
</td>
<td nowrap="nowrap">Jeffery Hicks</td>
<td>Challenge</td>
<td>advanced, beginner, challenge</td>
<td>The latest challenge from the Iron Scripter Chairman starts with a simple exercise aimed at PowerShell beginners. It ends with a more complex set of requirements for advanced PowerShell scripters. In between, you can embellish and add as much as you feel comfortable doing. The core of the challenge is based on the Get-NetTCPConnection cmdlet. …</td>
</tr>
<tr>
<td nowrap="nowrap">12/17/2019 5:20:52 PM</td>
<td nowrap="nowrap">12/17/2019 5:41:53 PM</td>
<td nowrap="nowrap">
<a href="https://ironscripter.us/a-powershell-challenge-for-challenges/">A PowerShell Challenge for Challenges</a>
</td>
<td nowrap="nowrap">Jeffery Hicks</td>
<td>Challenge, Iron Scripter</td>
<td>advanced, challenge, intermediate</td>
<td>Since the last PowerShell Summit, the Chairman has been offering PowerShell scripting challenges to help hone your skills. Because not everyone is an advanced PowerShell user, he has also been offering challenges for beginner and intermediate level professionals.  Which brings us to this challenge which is to find the previous challenges with PowerShell. The JSON …</td>
</tr>
</tbody>
</table><p class="footer">There are 15 posts with a category of Challenge.</p>
<table>
</table>
</body></html>
@thedavecarroll
Copy link
Author

thedavecarroll commented Dec 22, 2019

Challenge for Challenges

The latest IronScripter challenge, A PowerShell Challenge for Challenges, had challengers delving into JSON and REST APIs, specifically the WordPress REST API.

I'd worked with APIs before, but not for WordPress and I hadn't really dealt with pagination.

NOTE: I am using PowerShell 7 RC1 for this challenge.

Intermediate Challenge

For this part of the challenge, I'll use the functions I created for the advanced challenge.

First, import the basic script module. All functions are exported, as I did not use Export-ModuleMember.

Import-Module .\WordPressApi.psm1

Module Functions

Get-WPSiteLinks

This function is the really starting point. It will return all routes or the URL for a specific element.

Get-WPSiteLink -Url 'https://ironscripter.us' -Element Categories
https://ironscripter.us/wp-json/wp/v2/categories

Stay tuned. More to come.

Get-WPElement

Stay tuned. More to come.

Get-WPPost

Stay tuned. More to come.

Get-WPSite

Stay tuned. More to come.

Posts per Category

How many posts are there for each category?

Get-WPSiteLink -Url 'https://ironscripter.us' -Element Categories | Get-WPElement | Select Count,Name
count name
----- ----
   15 Challenge
   10 Iron Scripter
   16 Iron Scripter prelude
    0 Uncategorized

Posts for Beginner, Intermediate, and Advanced Tags

How many posts are there for the beginner,intermediate, and advanced tags?

Get-WPSiteLink -Url 'https://ironscripter.us' -Element Tags | Get-WPElement | Where { $_.Name -match 'beginner|intermediate|advanced'} | Select Count,Name
count name
----- ----
    9 advanced
    6 beginner
    5 intermediate

Display Posts for Challenge Category

Display posts for the Challenge category showing when it was posted, the title, the categories, the tags, excerpt, and link. Extra PowerShell Bonus points for displaying categories and tags with their names.

Get-WPPost -Url 'https://ironscripter.us' | where {$_.Categories -match 'Challenge'}
DateCreated  : 6/28/2019 2:55:39 PM
DateModified : 6/28/2019 2:55:39 PM
Status       : publish
Title        : A PowerShell Cryptogram
Link         : https://ironscripter.us/a-powershell-cryptogram/
Author       : Jeffery Hicks
Slug         : a-powershell-cryptogram
Categories   : Challenge
Tags         : advanced, challenge
Excerpt      : The Chairman is back with another challenge, this one probably more on the harder end. Although once you get your head around how to solve it, you’ll probably find it pretty easy. Your challenge is to solve a cryptogram like this, presumably using PowerShell. Inside this string block is a hidden message. To solve or …

Advanced Challenge

Display Last X Posts for Challenge Category

Include an option to get the newest X number of challenges.

Get-WPPost -Url 'https://ironscripter.us' -Last 2
DateCreated  : 11/27/2019 2:17:57 PM
DateModified : 12/2/2019 4:02:37 PM
Title        : Are You Listening to Me
Link         : https://ironscripter.us/are-you-listening-to-me/
Author       : Jeffery Hicks
Status       : publish
Slug         : are-you-listening-to-me
Categories   : Challenge
Tags         : advanced, beginner, challenge
Excerpt      : The latest challenge from the Iron Scripter Chairman starts with a simple exercise aimed at PowerShell beginners. It ends with a more complex set of requirements
               for advanced PowerShell scripters. In between, you can embellish and add as much as you feel comfortable doing. The core of the challenge is based on the
               Get-NetTCPConnection cmdlet. …

DateCreated  : 12/17/2019 5:20:52 PM
DateModified : 12/17/2019 5:41:53 PM
Title        : A PowerShell Challenge for Challenges
Link         : https://ironscripter.us/a-powershell-challenge-for-challenges/
Author       : Jeffery Hicks
Status       : publish
Slug         : a-powershell-challenge-for-challenges
Categories   : Challenge, Iron Scripter
Tags         : advanced, challenge, intermediate
Excerpt      : Since the last PowerShell Summit, the Chairman has been offering PowerShell scripting challenges to help hone your skills. Because not everyone is an advanced PowerShell user, he has also been offering challenges for beginner and intermediate level professionals. Which brings us to this challenge which is to find the previous challenges with PowerShell. The JSON …

Generate HTML Report

For an extra challenge, write a control script to create a pretty HTML page with links from the challenge post data.

This really was a challenge as I wanted to include some styling, like bold column headers and force the first four columns to not wrap (who wants to see a datetime object broken into separate lines?).

.\CreateChallengeReport.ps1 -FilePath .\Challenges.html

See the Challenges.html in this gist for the results.

The Challenges to Creating the HTML Report

Missing Posts

I realized I wasn't getting all of the posts. I had to modify the Get-WPElement function to get all available pages in succession.

Good Looking HTML

As I mentioned, I didn't want to provide a generic, out-of-the-box, report. I found Pure.CSS which helped at first, but I opted to go with a borrowed style (see below).

Table Woes

Next, the basic ConvertTo-Html -Fragment command that I was using to generate the table of posts threw in a <colgroup> element which I didn't want. I had to get rid of that. Oh, and remember the css I wanted to use? The shading for the table header only works if <thead> HTML tag is used. The ConvertTo-Html -Fragment does not insert <thead> or <tbody>.

I found a great blog post on Adding Style to PowerShell HTML Reports by @JeffHicks. Using some techniques within that post and a LOT of sweat and tears, I managed to remove the <colgroup> and add the <thead> or <tbody>. I even found a way to format the body using [System.Xml.Linq.XElement]::Parse() so it will have the line breaks that appeases my OCD.

The same post also had a decent enough <style> sheet, so I borrowed that and nudged it here and there.

Summary

Overall, this was a great challenge that flexed some JSON and REST muscles. And the way I did it, it also flexed some CSS and XML (very much so).

Please feel free to leave a comment.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment