Rename-LayoutFileForTFS.ps1

function Rename-LayoutFileForTFS {
    ###############################################################
    #
    # This function will rename source file to match <FILENAME>_<LAYOUT_TYPE>_<LAYOUT_ID>.<EXTENSION> format
    #
    # The Layout ID Tag is set in the Get-LayoutTagName() function
    #
    # TM MD
    #
    ###############################################################
    param (
        [parameter(Mandatory=$true)]
        [string]$layoutFileName,
        [parameter]
        [ValidateSet('RDL','DOCX')]
        [string]$layoutType,
        [switch]$keepOriginal
    )

    # initialisation
    $layoutId = ""

    $file = Get-Item $layoutFileName

    # check if layout is set and, if not, derive one from the file extension
    if (($layoutType -eq '') -or ($layoutType -eq $nill)) {
        $layoutTypeString = $file.Extension.ToString().ToUpper().TrimStart('.')
    } else {
      $layoutTypeString = $layoutType.ToString()
    }

    # get the layout ID from corresponding layout file type
    switch ($layoutTypeString) {
        'RDL' {$layoutId = Get-LayoutIDTAgValueForRDL -layoutFileName $layoutFileName}
        'DOCX' {$layoutId = Get-LayoutIDTAgValueForWRD -layoutFileName $layoutFileName}
        default {throw "Unable to determin the layout type for $layoutFileName..."}
    }

    # error if layout id is not identified
    if (($layoutId -eq '') -or ($layoutId -eq $nill)) {
        Write-Error "Unable to find $(Get-LayoutTagName) tag in the $layoutFileName..."
        break
    }

    # generate a new file name
    $newFileName = "$($file.FullName.TrimEnd($file.Extension))_$($layoutTypeString)_$($layoutId)$($file.Extension)"

    # rename or copy the file, depending on the switch
    if (!($keepOriginal)) {
        Rename-Item -Path $layoutFileName -NewName $newFileName
        Write-Output "$newFileName renamed to $newFileName..."
    } else {
        Copy-Item -Path $layoutFileName -Destination $newFileName
        Write-Output "$newFileName is created..."
    }
}

function Get-LayoutIDTAgValueForRDL {
    ###############################################################
    #
    # This function will find relevant variable in the RDL and return this variable value
    #
    # NB! This function will leave a copy of the file being renamed in the TEMP folder. Could not find a way to unlock the file after accessing it via Zip assembly.
    #
    # TM MD
    #
    ###############################################################
    param(
        [parameter(Mandatory=$true)]
        [string]$layoutFileName
    )

    $layoutFile = Get-Item $layoutFileName
    $xmlDoc = New-Object System.Xml.XmlDocument
    $xmlDoc.load($layoutFile)

    foreach ($var in $xmlDoc.GetElementsByTagName('Variable')) {
        if ($var.Name -eq $(Get-LayoutTagName)) {
            return $var.Value
        }
    }
}

function Get-LayoutIDTAgValueForWRD {
    ###############################################################
    #
    # This function will use the Zip assembly to find the core.xml file in the relevant .docx and will read the relevant tag in the keywords element and return it's value
    # The layout ID tag is stored as <TAGNAME>:<TAGVALUE>
    #
    # NB! This function will leave a copy of the file being renamed in the TEMP folder. Could not find a way to unlock the file after accessing it via Zip assembly.
    #
    # TM MD
    #
    ###############################################################
    param(
        [parameter(Mandatory=$true)]
        [string]$layoutFileName
    )

    # check .Net version
    $sysEnvVer = [System.Environment]::Version
    if (!($sysEnvVer.Major -ge 4 -and $sysEnvVer.Build -ge 30319)) {throw "DotNet Framework 4.5 is not installed"}

    # load Compression assembly to use ZipFile library
    [Void][Reflection.Assembly]::LoadWithPartialName('System.IO.Compression.FileSystem')

    # copy the item to the TEMP folder to avoid any corruption
    $tempFileName = "$env:TEMP\$([guid]::NewGuid())"
    copy-item -Path $layoutFileName -Destination $tempFileName

    # get core.xml from the .docx file (no extraction)
    $coreXml = [IO.Compression.ZipFile]::OpenRead($tempFileName).Entries | where "Name" -eq "custom.xml"

    # load core.xml content into xmlDom
    $xmlDoc = New-Object System.Xml.XmlDocument
    $xmlDoc.load($coreXml.Open())

    # find cp:keywords elements - this is where the tags are stored;
    foreach ($keyword in $xmlDoc.GetElementsByTagName('property')) {
        $keywordValue = $keyword.name
        if ($keywordValue -eq $(Get-LayoutTagName)) {
            return $keyword.lpwstr
        }
    }
}

function Get-LayoutTagName {
    return 'TM_LAYOUT_ID'
}

Export-ModuleMember -Function Rename-LayoutFileForTFS