Generate/Update KB table of contents article

Automates the creation and updating of a Table of Contents (TOC) article for both the global Knowledge Base (KB) and each company's KB in your Hudu instance. It generates a structured HTML representation of the KB folder hierarchy, linking to all relevant articles within the folders.

  • Global KB TOC: Generates a TOC for the global KB, listing all articles and folders.

  • Company-Specific TOC: Generates a TOC for each company's KB, organizing articles within their respective folder structures.

  • HTML Structure: Produces nested HTML lists for a clear hierarchical view of the documentation.

Visual depicting the TOC article that the script creates
# Generate_TOC.ps1
# This script automates the creation and updating of a Table of Contents (TOC) article 
# for both the global Knowledge Base (KB) and each company's KB in Hudu.
# Usage: ./Generate_TOC.ps1

# -------------------------------------------------------------------------
# User Environment
# -------------------------------------------------------------------------

# Before starting, you'll need to set 3 variables, just below their explanations.
# 1. Set your Azure Key Vault name
# 2. Set the name of your secret (which holds Hudu API key)
# 3. Set the URL of your Hudu instance-

$AzVault_Name = "ENTER YOUR KEY VAULT NAME HERE"
$AzVault_HuduSecretName = "ENTER YOUR KEY VAULT SECRET NAME HERE"
$HuduBaseURL = "HTTPS://YOUR.HUDU.DOMAIN"

# -------------------------------------------------------------------------
# Init Modules and Authenticate
# -------------------------------------------------------------------------
foreach ($module in @('Az.KeyVault', 'HuduAPI')) {
  if (Get-Module -ListAvailable -Name $module) { 
      Write-Host "Importing module, $module..."; 
      Import-Module $module 
  } else {
      Write-Host "Installing and importing module $module..."; 
      Install-Module $module -Force -AllowClobber; 
      Import-Module $module 
  }
}

if (-not (Get-AzContext)) { 
Connect-AzAccount };

New-HuduAPIKey "$(Get-AzKeyVaultSecret -VaultName "$AzVault_Name" -Name "$AzVault_HuduSecretName" -AsPlainText)"
New-HuduBaseUrl $HuduBaseURL

# -------------------------------------------------------------------------
# Begin Script Logic
# -------------------------------------------------------------------------

# Set the name of the Table of Contents article. 
$TOCArticleName = '000 - Table of Contents'

# Define the Get-ProcessedFolder function
function Get-ProcessedFolder {
    param (
        $FolderParentID,  # ID of the parent folder being processed
        $FolderName,      # Name of the folder
        $FolderDepth,     # Depth of the folder in the hierarchy (used for HTML heading levels)
        $Articles,        # Collection of articles to be included in the TOC
        $Folders,         # Collection of folders to be included in the TOC
        $HTML             # List object used to accumulate the HTML content
    )
    
    # Limit the depth of the folder structure to 6 levels to prevent excessively deep nesting
    if ($FolderDepth -gt 6) {
        $Depth = 6
    } else {
        $Depth = $FolderDepth
    }

    # Add the folder name as a heading if depth is greater than 0, otherwise just open a list
    if ($Depth -ne 0) {
        $HTML.add("<h$($Depth)>$FolderName</h$Depth><ul>")
    } else {
        $HTML.add("<ul>")
    }

    # Add each article within the current folder to the HTML list
    foreach ($Article in ($Articles | Where-Object { $FolderParentID -eq $_.folder_id } | Sort-Object name)) {
        $HTML.add("<li><a href='$($Article.url)'>$($Article.Name)</a></li>")
    }

    # Recursively process subfolders
    foreach ($Folder in ($Folders | Where-Object { $FolderParentID -eq $_.parent_folder_id } | Sort-Object name)) {
        Get-ProcessedFolder -FolderParentID $Folder.id -FolderName $Folder.name -FolderDepth ($FolderDepth + 1) -Articles $Articles -Folders $Folders -HTML $HTML
    }

    # Close the HTML list
    $HTML.add("</ul><br />")
}

# Retrieve all non-archived articles and all folders
$AllArticles = Get-HuduArticles | Where-Object { $_.archived -eq $false }
$Folders = Get-HuduFolders

# Retrieve any existing articles with the specified TOC name
$TableOfContentsArticles = Get-HuduArticles -Name $TOCArticleName

# Process the Global KB First
$GlobalKBs = $AllArticles | Where-Object { $_.company_id -eq $null }
$GlobalFolders = $Folders | Where-Object { $_.company_id -eq $null }

# Initialize an empty list to hold the HTML for the global TOC
[System.Collections.Generic.List[PSCustomObject]]$GlobalHTML = @()
$GlobalHTML.add('<h1 class="align-center">' + $TOCArticleName + '</h1>')

# Generate the HTML for the global KB
Get-ProcessedFolder -FolderParentID $null -FolderName "Top Level Documents" -FolderDepth 0 -Articles $GlobalKBs -Folders $GlobalFolders -HTML $GlobalHTML

# Check if the global TOC article already exists and create or update it as necessary
$KBArticle = $TableOfContentsArticles | Where-Object { $_.company_id -eq $null }
$KBCount = ($KBArticle | Measure-Object).count
if ($KBCount -eq 0) {
    # Create a new TOC article if it doesn't exist
    New-HuduArticle -Name $TOCArticleName -Content ($GlobalHTML -join '')
    Write-Host "Created a new global TOC article." -ForegroundColor Green
} elseif ($KBCount -eq 1) {
    # Update the existing TOC article if it exists
    Set-HuduArticle -ArticleId $KBArticle.Id -Content ($GlobalHTML -join '') -Name $TOCArticleName
    Write-Host "Updated the existing global TOC article." -ForegroundColor Yellow
} else {
    # Raise an error if multiple TOC articles with the same name are found
    Write-Error "Multiple KB Articles found with the same name."
}

# Generate the TOC for each company
foreach ($CompanyID in ($AllArticles.company_id | Select-Object -Unique)) {
    $CompanyArticles = $AllArticles | Where-Object { $_.company_id -eq $CompanyID }
    $CompanyFolders = $Folders | Where-Object { $_.company_id -eq $CompanyID }

    # Initialize an empty list to hold the HTML for the company's TOC
    [System.Collections.Generic.List[PSCustomObject]]$CompanyHTML = @()
    $CompanyHTML.add('<h1 class="align-center">' + $TOCArticleName + '</h1>')

    # Generate the HTML for the company's KB
    Get-ProcessedFolder -FolderParentID $null -FolderName "Top Level Documents" -FolderDepth 0 -Articles $CompanyArticles -Folders $CompanyFolders -HTML $CompanyHTML

    # Check if the company's TOC article already exists and create or update it as necessary
    $CompanyArticle = $TableOfContentsArticles | Where-Object { $_.company_id -eq $CompanyID }
    $CompanyCount = ($CompanyArticle | Measure-Object).count
    if ($CompanyCount -eq 0) {
        New-HuduArticle -Name $TOCArticleName -Content ($CompanyHTML -join '') -company_id $CompanyID
        Write-Host "Created a new TOC article for Company ID $CompanyID." -ForegroundColor Green
    } elseif ($CompanyCount -eq 1) {
        Set-HuduArticle -ArticleId $CompanyArticle.Id -Content ($CompanyHTML -join '') -Name $TOCArticleName -company_id $CompanyID
        Write-Host "Updated the existing TOC article for Company ID $CompanyID." -ForegroundColor Yellow
    } else {
        Write-Error "Multiple KB Articles found with the same name - Company ID: $CompanyID."
    }
}
Remove-HuduAPIKey

Script Data

  • Language: PowerShell

  • Run As: Logged In User

  • Script Timeout Duration: 5 Mins

Prerequisites:

  • Ensure you have a valid Hudu API key with sufficient permissions to create and update articles.

  • Customize the TOC article name ($TOCArticleName) as needed before running.

Script credit: https://mspp.io/powershell-hudu-table-of-contents/

8