Last active
December 22, 2019 18:49
-
-
Save thedavecarroll/5273424745bf4dfc57c08b62bd53a2b5 to your computer and use it in GitHub Desktop.
IronScripter Challenge - December 17, 2019 - A PowerShell Challenge for Challenges
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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()}} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
[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 | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!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> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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.
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.
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
Posts for Beginner, Intermediate, and Advanced Tags
Display Posts for Challenge Category
Advanced Challenge
Display Last X Posts for Challenge Category
Generate HTML Report
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. TheConvertTo-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.