XurrentHelpers.psm1

#Region './Private/Get-Section.ps1' -1

function Get-Section
{
    <#
        .SYNOPSIS
        Extracts the body of a named Markdown H2 section.

        .DESCRIPTION
        Returns the trimmed text body of a ## Heading section from a Markdown string.
        Matches content from the heading line to the next same-level ## heading or end of string.
        Returns an empty string when the requested section is not found.

        .PARAMETER Content
        The full Markdown document content as a string.

        .PARAMETER Heading
        The section heading to search for, without the leading ## prefix.

        .EXAMPLE
        Get-Section -Content $markdown -Heading 'Description'
    #>

    [CmdletBinding()]
    [OutputType([string])]
    param
    (
        [Parameter(Mandatory = $true)]
        [string]
        $Content,

        [Parameter(Mandatory = $true)]
        [string]
        $Heading
    )

    process
    {
        $pattern = "(?ms)^##\s+$([regex]::Escape($Heading))\s*$\r?\n(.*?)(?=\r?\n^##\s+|\z)"
        if ($Content -match $pattern)
        {
            return $Matches[1].Trim()
        }
        return ''
    }
}
#EndRegion './Private/Get-Section.ps1' 44
#Region './Public/ConvertTo-XurrentKnowledgeArticle.ps1' -1

function ConvertTo-XurrentKnowledgeArticle
{
    <#
        .SYNOPSIS
        Converts a Markdown knowledge article file to a Xurrent import object.

        .DESCRIPTION
        Reads a *KnowledgeArticle.md file and extracts the Subject (first H1 heading),
        Description (## Description section), Instructions (## Instructions section),
        and Keywords (**Keywords:** line) into a PSCustomObject formatted for the
        Xurrent / 4me bulk-import CSV schema.

        .PARAMETER File
        The input file to convert. Supports FileInfo, path strings, and objects that
        expose a FullName or Path property. Accepts pipeline input.

        .PARAMETER Service
        The Xurrent service name to write to the Service column of the import row.

        .PARAMETER ServiceInstances
        The Xurrent service instance name(s) for the Service Instances column.

        .EXAMPLE
        Get-Item .\MyAppKnowledgeArticle.md | ConvertTo-XurrentKnowledgeArticle -Service 'techwork automator' -ServiceInstances 'techwork automator for ACS'

        .EXAMPLE
        Get-ChildItem -Recurse -Filter '*KnowledgeArticle.md' | ConvertTo-XurrentKnowledgeArticle -Service 'my svc' -ServiceInstances 'my inst'
    #>

    [CmdletBinding()]
    [OutputType([PSCustomObject])]
    param
    (
        [Parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
        [Alias('FullName', 'Path', 'PSPath')]
        [object]
        $File,

        [Parameter(Mandatory = $true)]
        [string]
        $Service,

        [Parameter(Mandatory = $true)]
        [string]
        $ServiceInstances
    )

    process
    {
        $filePath = $null
        if ($File -is [System.IO.FileInfo])
        {
            $filePath = $File.FullName
        }
        elseif ($File -is [string])
        {
            $filePath = $File
        }
        elseif ($null -ne $File.PSObject.Properties['FullName'])
        {
            $filePath = [string]$File.FullName
        }
        elseif ($null -ne $File.PSObject.Properties['Path'])
        {
            $filePath = [string]$File.Path
        }

        if (-not $filePath)
        {
            throw 'File must be a path string, FileInfo, or an object with FullName/Path.'
        }

        $resolvedFile = Get-Item -LiteralPath $filePath -ErrorAction Stop
        if ($resolvedFile -isnot [System.IO.FileInfo])
        {
            throw "Input path '$filePath' is not a file."
        }

        $raw = Get-Content -Path $resolvedFile.FullName -Raw -Encoding UTF8

        $subject = ''
        if ($raw -match '(?m)^#\s+(.+)$') { $subject = $Matches[1].Trim() }

        $keywords = ''
        if ($raw -match '\*\*Keywords:\*\*\s*(.+)') { $keywords = $Matches[1].Trim() }

        [PSCustomObject]@{
            ID                  = ''
            Source              = '4me'
            'Source ID'         = ''
            Status              = 'work_in_progress'
            Service             = $Service
            'Service Instances' = $ServiceInstances
            Subject             = $subject
            Description         = Get-Section -Content $raw -Heading 'Description'
            Instructions        = Get-Section -Content $raw -Heading 'Instructions'
            Keywords            = $keywords
            Template            = ''
        }
    }
}
#EndRegion './Public/ConvertTo-XurrentKnowledgeArticle.ps1' 101
#Region './Public/Export-XurrentKnowledgeArticle.ps1' -1

function Export-XurrentKnowledgeArticle
{
    <#
        .SYNOPSIS
        Exports Xurrent knowledge articles from Markdown files to a CSV import file.

        .DESCRIPTION
        Scans a folder recursively for *KnowledgeArticle.md files and writes a CSV in the
        Xurrent / 4me bulk-import format. Subject is read from the first H1 heading,
        Description and Instructions from same-named ## sections, Keywords from a
        **Keywords:** line. Service and ServiceInstances can be set via parameters or
        loaded from a .env file (SERVICE and SERVICE_INSTANCES keys).

        .PARAMETER Folder
        Path to the folder to scan recursively for *KnowledgeArticle.md files.

        .PARAMETER Service
        Xurrent service name written to the Service column of every export row.
        Defaults to 'techwork automator'. Can also be supplied via SERVICE= in a .env file.

        .PARAMETER ServiceInstances
        Xurrent service instance name(s) for the Service Instances column.
        Can be supplied via SERVICE_INSTANCES= in a .env file; prompts interactively if still empty.

        .PARAMETER OutputPath
        Full path of the CSV file to write.
        Defaults to import-knowledge_articles.csv in the current working directory.

        .PARAMETER EnvFile
        Path to a .env file supplying SERVICE and SERVICE_INSTANCES defaults.
        Defaults to .env in the current working directory when that file exists.

        .EXAMPLE
        Export-KnowledgeArticle -Folder .\ACS -Service 'techwork automator' -ServiceInstances 'techwork automator for ACS'

        .EXAMPLE
        Export-KnowledgeArticle -Folder .\TTTech
        Loads SERVICE and SERVICE_INSTANCES from .env in the current directory.
    #>

    [CmdletBinding(SupportsShouldProcess = $true)]
    param
    (
        [Parameter(Mandatory = $true)]
        [ValidateScript({ Test-Path $_ -PathType Container })]
        [string]
        $Folder,

        [Parameter()]
        [string]
        $Service = 'techwork automator',

        [Parameter()]
        [string]
        $ServiceInstances = '',

        [Parameter()]
        [string]
        $OutputPath = (Join-Path (Get-Location).Path 'import-knowledge_articles.csv'),

        [Parameter()]
        [string]
        $EnvFile = (Join-Path (Get-Location).Path '.env')
    )

    process
    {
        if (Test-Path $EnvFile)
        {
            foreach ($line in (Get-Content $EnvFile))
            {
                if ($line -match "^([A-Z_]+)\s*=\s*'?([^']*?)'?\s*$")
                {
                    $key = $Matches[1]
                    $val = $Matches[2]
                    if ($key -eq 'SERVICE' -and -not $PSBoundParameters.ContainsKey('Service')) { $Service = $val }
                    if ($key -eq 'SERVICE_INSTANCES' -and -not $PSBoundParameters.ContainsKey('ServiceInstances')) { $ServiceInstances = $val }
                }
            }
        }

        if (-not $Service)
        {
            $Service = Read-Host -Prompt "Enter SERVICE name (e.g. 'techwork automator')"
        }
        if (-not $ServiceInstances)
        {
            $ServiceInstances = Read-Host -Prompt 'Enter SERVICE_INSTANCES'
        }

        $files = Get-ChildItem -Path $Folder -Recurse -Filter '*KnowledgeArticle.md'

        if ($files.Count -eq 0)
        {
            Write-Warning "No *KnowledgeArticle.md files found in $Folder"
            return
        }

        Write-Verbose "Found $($files.Count) knowledge article(s) in $Folder"

        $rows = foreach ($file in $files)
        {
            Write-Verbose " Processing $($file.FullName)"
            ConvertTo-XurrentKnowledgeArticle -File $file -Service $Service -ServiceInstances $ServiceInstances
        }

        if ($PSCmdlet.ShouldProcess($OutputPath, 'Export CSV'))
        {
            # PS 7+ needs utf8BOM for a BOM-prefixed file; PS 5.1 UTF8 already includes one.
            $encoding = if ($PSVersionTable.PSVersion.Major -ge 7) { 'utf8BOM' } else { 'UTF8' }
            $rows | Export-Csv -Path $OutputPath -NoTypeInformation -Encoding $encoding
            Write-Verbose "Exported $($rows.Count) article(s) to $OutputPath"
        }
    }
}
#EndRegion './Public/Export-XurrentKnowledgeArticle.ps1' 115
#Region './Public/New-XurrentKnowledgeArticleCsvExample.ps1' -1

function New-XurrentKnowledgeArticleCsvExample
{
    <#
        .SYNOPSIS
        Creates an example CSV file in the Xurrent knowledge article import format.

        .DESCRIPTION
        Writes a CSV file with all columns required by the Xurrent / 4me bulk-import schema
        for knowledge articles, pre-filled with one illustrative example row. Use it as a
        reference or starting point before populating real data via Export-KnowledgeArticle.

        .PARAMETER OutputPath
        Full path of the CSV file to write.
        Defaults to example-knowledge_articles.csv in the current working directory.

        .EXAMPLE
        New-KnowledgeArticleCsvExample
        Creates example-knowledge_articles.csv in the current directory.

        .EXAMPLE
        New-KnowledgeArticleCsvExample -OutputPath C:\Temp\example.csv
        Creates the example CSV at the specified path.
    #>

    [CmdletBinding(SupportsShouldProcess = $true)]
    [OutputType([System.IO.FileInfo])]
    param
    (
        [Parameter()]
        [string]
        $OutputPath = (Join-Path (Get-Location).Path 'example-knowledge_articles.csv')
    )

    process
    {
        $example = [PSCustomObject]@{
            ID                  = ''
            Source              = '4me'
            'Source ID'         = ''
            Status              = 'work_in_progress'
            Service             = 'my service'
            'Service Instances' = 'my service instance'
            Subject             = 'Example knowledge article subject'
            Description         = 'A brief description of the knowledge article.'
            Instructions        = 'Step 1: Do this. Step 2: Do that.'
            Keywords            = 'example, keyword1, keyword2'
            Template            = ''
        }

        if ($PSCmdlet.ShouldProcess($OutputPath, 'Create example knowledge article CSV'))
        {
            $encoding = if ($PSVersionTable.PSVersion.Major -ge 7) { 'utf8BOM' } else { 'UTF8' }
            $example | Export-Csv -Path $OutputPath -NoTypeInformation -Encoding $encoding
            Get-Item -Path $OutputPath
        }
    }
}
#EndRegion './Public/New-XurrentKnowledgeArticleCsvExample.ps1' 57
#Region './Public/New-XurrentKnowledgeArticleTemplate.ps1' -1

function New-XurrentKnowledgeArticleTemplate
{
    <#
        .SYNOPSIS
        Creates a Markdown template file for a Xurrent knowledge article.

        .DESCRIPTION
        Writes a *KnowledgeArticle.md template to the specified folder with the correct
        structure expected by ConvertTo-KnowledgeArticle: an H1 heading for the Subject,
        a **Keywords:** line, and ## Description and ## Instructions sections.

        .PARAMETER Name
        The name of the knowledge article, used as the H1 heading and filename prefix.
        The file is written as <Name>KnowledgeArticle.md.

        .PARAMETER Path
        The folder in which to create the template file.
        Defaults to the current working directory.

        .EXAMPLE
        New-KnowledgeArticleTemplate -Name 'HowToResetPassword'
        Creates HowToResetPasswordKnowledgeArticle.md in the current directory.

        .EXAMPLE
        New-KnowledgeArticleTemplate -Name 'VPN Setup' -Path C:\Articles
        Creates 'VPN SetupKnowledgeArticle.md' in C:\Articles.
    #>

    [CmdletBinding(SupportsShouldProcess = $true)]
    [OutputType([System.IO.FileInfo])]
    param
    (
        [Parameter(Mandatory = $true)]
        [string]
        $Name,

        [Parameter()]
        [ValidateScript({ Test-Path $_ -PathType Container })]
        [string]
        $Path = (Get-Location).Path
    )

    process
    {
        $fileName = '{0}KnowledgeArticle.md' -f $Name
        $filePath = Join-Path -Path $Path -ChildPath $fileName

        $template = @"
# $Name

**Keywords:** keyword1, keyword2

## Description

Describe the knowledge article here.

## Instructions

Provide step-by-step instructions here.
"@


        if ($PSCmdlet.ShouldProcess($filePath, 'Create knowledge article template'))
        {
            Set-Content -Path $filePath -Value $template -Encoding UTF8
            Get-Item -Path $filePath
        }
    }
}
#EndRegion './Public/New-XurrentKnowledgeArticleTemplate.ps1' 68